From 58f5248934522ac97e3c4547b4f3ecd62bde28ea Mon Sep 17 00:00:00 2001 From: Harley Eades Date: Sat, 14 Dec 2019 10:54:23 -0500 Subject: [PATCH 01/46] Adds the ability to rename inference rules when Tex filtering. --- src/defns.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/defns.ml b/src/defns.ml index b16712c..e0eb55d 100644 --- a/src/defns.ml +++ b/src/defns.ml @@ -233,6 +233,10 @@ let pp_drule fd (m:pp_mode) (xd:syntaxdefn) (dr:drule) : unit = "\\text{" ^ String.concat "" (Grammar_pp.apply_hom_spec m xd hs []) ^ "}") in + Printf.fprintf fd "\\newcommand{%sName}[0]{%s{%s}}\n" + (Grammar_pp.tex_drule_name m dr.drule_name) + (Grammar_pp.pp_tex_DRULE_NAME_NAME m) + (Auxl.pp_tex_escape dr.drule_name); Printf.fprintf fd "\\newcommand{%s}[1]{%s[#1]{%%\n" (Grammar_pp.tex_drule_name m dr.drule_name) (Grammar_pp.pp_tex_DRULE_NAME m); From b1ad0ebb5c1f3ffeab4a6deee7c4b22a9add6701 Mon Sep 17 00:00:00 2001 From: Harley Eades Date: Mon, 16 Nov 2020 01:58:11 -0500 Subject: [PATCH 02/46] Trims whitespace from around custom homs that interfers with Pandoc. --- src/embed_pp.ml | 2 +- src/grammar_pp.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/embed_pp.ml b/src/embed_pp.ml index 1ab5ef9..52e93fc 100644 --- a/src/embed_pp.ml +++ b/src/embed_pp.ml @@ -96,4 +96,4 @@ and pp_embed_spec_el fd m xd lookup ese = (* not taking account of possible dot forms shared over different terms *) let st = Term_parser.just_one_parse xd lookup "user_syntax" false l s in let ((de1,de2) as de,de3,pptbe) = Bounds.bound_extraction m xd l [st] in - output_string fd (Grammar_pp.pp_symterm m xd [] de st)) + output_string fd (String.trim (Grammar_pp.pp_symterm m xd [] de st))) diff --git a/src/grammar_pp.ml b/src/grammar_pp.ml index d0ecd0c..72a5f3c 100644 --- a/src/grammar_pp.ml +++ b/src/grammar_pp.ml @@ -1363,7 +1363,7 @@ and apply_hom_spec m xd hses ss = match hses with | [] -> [] | Hom_string s :: hses' -> - s :: apply_hom_spec m xd hses' ss + String.trim s :: apply_hom_spec m xd hses' ss | Hom_index i :: hses' -> (List.nth ss i) :: apply_hom_spec m xd hses' ss | Hom_terminal tm :: hses' -> From db4f53e4a995efa83ed6763a3593e056a0406797 Mon Sep 17 00:00:00 2001 From: Harley Eades Date: Tue, 24 Nov 2020 11:17:22 -0600 Subject: [PATCH 03/46] Removed a change that is not needed. --- src/embed_pp.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embed_pp.ml b/src/embed_pp.ml index 52e93fc..1ab5ef9 100644 --- a/src/embed_pp.ml +++ b/src/embed_pp.ml @@ -96,4 +96,4 @@ and pp_embed_spec_el fd m xd lookup ese = (* not taking account of possible dot forms shared over different terms *) let st = Term_parser.just_one_parse xd lookup "user_syntax" false l s in let ((de1,de2) as de,de3,pptbe) = Bounds.bound_extraction m xd l [st] in - output_string fd (String.trim (Grammar_pp.pp_symterm m xd [] de st))) + output_string fd (Grammar_pp.pp_symterm m xd [] de st)) From c94aa62fa4a5d4e0114b36beb890ecc50a24694a Mon Sep 17 00:00:00 2001 From: Harley Eades Date: Tue, 24 Nov 2020 11:33:54 -0600 Subject: [PATCH 04/46] Undid the wrong patch. This adds in the patch for fixing whitespace issues when using ott with pandoc. --- src/embed_pp.ml | 2 +- src/grammar_pp.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/embed_pp.ml b/src/embed_pp.ml index 1ab5ef9..52e93fc 100644 --- a/src/embed_pp.ml +++ b/src/embed_pp.ml @@ -96,4 +96,4 @@ and pp_embed_spec_el fd m xd lookup ese = (* not taking account of possible dot forms shared over different terms *) let st = Term_parser.just_one_parse xd lookup "user_syntax" false l s in let ((de1,de2) as de,de3,pptbe) = Bounds.bound_extraction m xd l [st] in - output_string fd (Grammar_pp.pp_symterm m xd [] de st)) + output_string fd (String.trim (Grammar_pp.pp_symterm m xd [] de st))) diff --git a/src/grammar_pp.ml b/src/grammar_pp.ml index 72a5f3c..d0ecd0c 100644 --- a/src/grammar_pp.ml +++ b/src/grammar_pp.ml @@ -1363,7 +1363,7 @@ and apply_hom_spec m xd hses ss = match hses with | [] -> [] | Hom_string s :: hses' -> - String.trim s :: apply_hom_spec m xd hses' ss + s :: apply_hom_spec m xd hses' ss | Hom_index i :: hses' -> (List.nth ss i) :: apply_hom_spec m xd hses' ss | Hom_terminal tm :: hses' -> From 84d2f784a31b70d5715000d0446122f995b17fde Mon Sep 17 00:00:00 2001 From: Harley Eades Date: Tue, 23 Feb 2021 16:10:19 -0500 Subject: [PATCH 05/46] Fixed a bug in the renaming. Thanks @dorchard. --- src/defns.ml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/defns.ml b/src/defns.ml index 74a9e95..37f99d1 100644 --- a/src/defns.ml +++ b/src/defns.ml @@ -245,9 +245,8 @@ let pp_drule fd (m:pp_mode) (xd:syntaxdefn) (dr:drule) : unit = (snd ppd_premises); output_string fd "}{\n"; output_string fd ppd_conclusion; - Printf.fprintf fd "}{%%\n{%s{%s}}{%s}%%\n}}\n" - (Grammar_pp.pp_tex_DRULE_NAME_NAME m) - (Auxl.pp_tex_escape dr.drule_name) + Printf.fprintf fd "}{%%\n{%sName}{%s}%%\n}}\n" + (Grammar_pp.tex_drule_name m dr.drule_name) pp_com | Isa _ | Hol _ | Lem _ | Coq _ | Twf _ -> let non_free_ntrs = Subrules_pp.non_free_ntrs m xd xd.xd_srs in From fff9fc9dbe969c0d58296b746161027f3302b27b Mon Sep 17 00:00:00 2001 From: Peter Hanukaev Date: Fri, 5 Aug 2022 17:51:20 -0400 Subject: [PATCH 06/46] Identifiers can now contain greek characters --- src/grammar_lexer.mll | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/grammar_lexer.mll b/src/grammar_lexer.mll index 46b7a31..8bcdea3 100644 --- a/src/grammar_lexer.mll +++ b/src/grammar_lexer.mll @@ -207,7 +207,9 @@ let newline = ('\010' | '\013' | "\013\010") let non_newline = [^ '\010' '\013'] let non_newline_whitespace = (' ' | '\t' |'\012') (* let pre_ident = (['A'-'Z'] | ['a'-'z'] | ['0'-'9'] | "_" | "'")(['A'-'Z'] | ['a'-'z'] | ['0'-'9'] | "_" | "-" | "'")* *) -let pre_ident = (['A'-'Z'] | ['a'-'z'] | ['0'-'9'] | "_" | "'")(['A'-'Z'] | ['a'-'z'] | ['0'-'9'] | "_" | "'")* +(* let pre_ident = (['A'-'Z'] | ['a'-'z'] | ['0'-'9'] | "_" | "'")(['A'-'Z'] | ['a'-'z'] | ['0'-'9'] | "_" | "'")* *) +let pre_ident = (['A'-'Z'] | ['a'-'z'] | ['0'-'9'] | "_" | "'" | "\u{0391}" | "\u{0392}" | "\u{0393}" | "\u{0394}" | "\u{0395}" | "\u{0396}" | "\u{0397}" | "\u{0398}" | "\u{0399}" | "\u{039a}" | "\u{039b}" | "\u{039c}" | "\u{039d}" | "\u{039e}" | "\u{039f}" | "\u{03a0}" | "\u{03a1}" | "\u{03a3}" | "\u{03a4}" | "\u{03a5}" | "\u{03a6}" | "\u{03a7}" | "\u{03a8}" | "\u{03a9}" | "\u{03b1}" | "\u{03b2}" | "\u{03b3}" | "\u{03b4}" | "\u{03b5}" | "\u{03b6}" | "\u{03b7}" | "\u{03b8}" | "\u{03b9}" | "\u{03ba}" | "\u{03bb}" | "\u{03bc}" | "\u{03bd}" | "\u{03be}" | "\u{03bf}" | "\u{03c0}" | "\u{03c1}" | "\u{03c3}" | "\u{03c4}" | "\u{03c5}" | "\u{03c6}" | "\u{03c7}" | "\u{03c8}" | "\u{03c9}" )+ +(*(['A'-'Z'] | ['a'-'z'] | ['0'-'9'] | "_" | "'")* *) let maybe_quoted_ident = pre_ident | ("'" pre_ident "'" ) | "''" (* let pre_ident_allow_minus = (['A'-'Z'] | ['a'-'z'] | ['0'-'9'] | "_" | "'")(['A'-'Z'] | ['a'-'z'] | ['0'-'9'] | "_" | "-" | "'")* From bfd9e93986d1b5875d433186e027c9e3298bbc04 Mon Sep 17 00:00:00 2001 From: pes20 Date: Tue, 16 Mar 2021 16:32:45 +0000 Subject: [PATCH 07/46] fix README broken link found by Shaked --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2235479..5bccdfb 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ As of 2020, Ott remains in continuous use. (html)](http://www.cl.cam.ac.uk/~pes20/ott/top2.html) - [Binding and - Substitution](http://www.cl.cam.ac.uk/users/pes20/bind-doc.pdf). Susmit + Substitution](http://www.cl.cam.ac.uk/users/pes20/ott/bind-doc.pdf). Susmit Sarkar, Peter Sewell, Francesco Zappa Nardelli. Note, 2007. - [The experimental Coq locally-nameless backend From 7e27d38d0f527e707224ec9c770029a6735bfb45 Mon Sep 17 00:00:00 2001 From: Brian Campbell Date: Wed, 17 Mar 2021 14:45:59 +0000 Subject: [PATCH 08/46] Make JSON pp optional, add warnings/errors on missing pp homs (prevents missing JSON homs breaking the whole pp output) --- src/global_option.ml | 2 ++ src/lex_menhir_pp.ml | 12 ++++++------ src/main.ml | 3 +++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/global_option.ml b/src/global_option.ml index 51c3708..db9b34b 100644 --- a/src/global_option.ml +++ b/src/global_option.ml @@ -42,3 +42,5 @@ let output_source_locations = ref 0 let aux_style_rules = ref true let caml_pp_ast_module = ref (None : string option) + +let caml_pp_json = ref false diff --git a/src/lex_menhir_pp.ml b/src/lex_menhir_pp.ml index 302824b..bdfcf2e 100644 --- a/src/lex_menhir_pp.ml +++ b/src/lex_menhir_pp.ml @@ -1037,7 +1037,7 @@ let pp_pp_rule yo generate_aux_info prettier xd ts r = | None -> if r.rule_phantom then (* hack: default pp hom if missing *) - (*(Auxl.error (Some r.rule_loc) ("no pp hom for phantom production "^r.rule_ntr_name));*) + let () = Auxl.warning (Some r.rule_loc) ("no pp hom for phantom production "^r.rule_ntr_name) in Some (pp_pp_name r.rule_ntr_name ^ "_default " (*^ Grammar_pp.pp_hom_spec (Menhir yo) xd hs *)^"\n\n") else let generate_aux_info_here = generate_aux_info_for_rule generate_aux_info r in @@ -1121,8 +1121,7 @@ let pp_pp_json_rule yo generate_aux_info xd ts r = Some (pp_pp_json_name r.rule_ntr_name ^ " " ^ Grammar_pp.pp_hom_spec (Menhir yo) xd hs ^"\n\n") | None -> if r.rule_phantom then - (*(Auxl.error (Some r.rule_loc) ("no pp-json hom for phantom production "^r.rule_ntr_name));*) - Some (pp_pp_json_name r.rule_ntr_name ^ "_default " (*^ Grammar_pp.pp_hom_spec (Menhir yo) xd hs*) ^"\n\n") + Auxl.error (Some r.rule_loc) ("no pp-json hom for phantom production "^r.rule_ntr_name) else let generate_aux_info_here = generate_aux_info_for_rule generate_aux_info r in @@ -1235,9 +1234,10 @@ let pp_pp_syntaxdefn m sources xd_quotiented xd_unquotiented xd_quotiented_unaux output_string fd ("open " ^ (match !Global_option.caml_pp_ast_module with None -> yo.ppm_caml_ast_module | Some s -> s) ^ "\n\n" ^ pp_pp_raw_metavar_defns_and_rules yo generate_aux_info xd ts xd.xd_mds xd.xd_rs ^ "\n" - ^ pp_pp_metavar_defns_and_rules yo generate_aux_info xd ts xd.xd_mds xd.xd_rs - ^ "\n" - ^ pp_pp_json_metavar_defns_and_rules yo generate_aux_info xd ts xd.xd_mds xd.xd_rs + ^ pp_pp_metavar_defns_and_rules yo generate_aux_info xd ts xd.xd_mds xd.xd_rs); + if !Global_option.caml_pp_json then + output_string fd ("\n" + ^ pp_pp_json_metavar_defns_and_rules yo generate_aux_info xd ts xd.xd_mds xd.xd_rs ); close_out fd; diff --git a/src/main.ml b/src/main.ml index 231c3a5..74011e0 100644 --- a/src/main.ml +++ b/src/main.ml @@ -259,6 +259,9 @@ let options = Arg.align [ Arg.String (fun s -> Global_option.caml_pp_ast_module := Some s), " override default OCaml module name for AST module (experimental!)" ); + ( "-ocaml_pp_json", + Arg.Bool (fun b -> Global_option.caml_pp_json := b), + "<"^string_of_bool !Global_option.caml_pp_json^"> Include JSON output in pretty printer (experimental)"); (* options for debugging *) ( "-pp_grammar", From c3edcbb8d01a823bfe91b0da133ba0ea8973d35a Mon Sep 17 00:00:00 2001 From: pes20 Date: Wed, 21 Jul 2021 08:21:35 +0100 Subject: [PATCH 09/46] fix X documentation --- doc/top2.mng | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/top2.mng b/doc/top2.mng index 3355974..4c48823 100644 --- a/doc/top2.mng +++ b/doc/top2.mng @@ -300,8 +300,8 @@ away when generating proof assistant output. The two flags \mykw{M} and \mykw{S} are identical except that productions with the latter are admitted when parsing example concrete terms; the \mykw{S} tag is thus appropriate for lightweight syntactic sugar, such as productions for -parentheses. One can also use an \mykw{X} flag here to suppress a -production in the generated LaTeX. +parentheses. One can also use another flag, e.g.~\mykw{X}, along with the command-line option \verb+-tex_suppress_category+, to suppress +productions in the generated LaTeX. Each production has a production name (e.g.~\verb+t_Lam+), composed of the rule name prefix (here~\verb+t_+) and the production name kernel From 215f7d2b04e083f936cc8d373e0d23124f05d724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20P=C3=A9rami?= Date: Thu, 3 Mar 2022 11:46:37 +0000 Subject: [PATCH 10/46] Fix make clean --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bf90f36..0c203fd 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ world.byt: clean: cd src; $(MAKE) clean rm -f *~ - rm ott.install + rm -f ott.install # ott.install file ###################################################### From 60eeb799bdb4e874f890d4b0d700d5b00a5dd942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20P=C3=A9rami?= Date: Thu, 3 Mar 2022 11:50:23 +0000 Subject: [PATCH 11/46] Remove executability of tests/test-mjp.ott --- tests/test-mjp.ott | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 tests/test-mjp.ott diff --git a/tests/test-mjp.ott b/tests/test-mjp.ott old mode 100755 new mode 100644 From 4738a9ae32e2dd8cdd74ae0eaa55d896cdc1580a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Pottier?= Date: Mon, 3 Jan 2022 15:49:46 +0100 Subject: [PATCH 12/46] Refer to PPrint instead of PPrintEngine and PPrintCombinators. --- src/lex_menhir_pp.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lex_menhir_pp.ml b/src/lex_menhir_pp.ml index bdfcf2e..8912cd4 100644 --- a/src/lex_menhir_pp.ml +++ b/src/lex_menhir_pp.ml @@ -1230,7 +1230,7 @@ let pp_pp_syntaxdefn m sources xd_quotiented xd_unquotiented xd_quotiented_unaux *) let fd = open_out filename in Printf.fprintf fd "(* generated by Ott %s from: %s *)\n" Version.n sources; - output_string fd "open PPrintEngine\nopen PPrintCombinators\n"; + output_string fd "open PPrint\n"; output_string fd ("open " ^ (match !Global_option.caml_pp_ast_module with None -> yo.ppm_caml_ast_module | Some s -> s) ^ "\n\n" ^ pp_pp_raw_metavar_defns_and_rules yo generate_aux_info xd ts xd.xd_mds xd.xd_rs ^ "\n" From a80f257961edcca31d5563d741d6dc7c11032cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Pottier?= Date: Mon, 3 Jan 2022 15:54:43 +0100 Subject: [PATCH 13/46] tests/menhir_tests: refer to PPrint instead of PPrintEngine. --- tests/menhir_tests/test10menhir/main.ml | 4 ++-- tests/menhir_tests/test10menhir_with_aux_args/main.ml | 4 ++-- tests/menhir_tests/test10menhir_with_aux_rules/main.ml | 6 +++--- tests/menhir_tests/test_if/main.ml | 4 ++-- tests/menhir_tests/test_lists/main.ml | 4 ++-- tests/menhir_tests/test_phantom/main.ml | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/menhir_tests/test10menhir/main.ml b/tests/menhir_tests/test10menhir/main.ml index 0e30740..8303193 100644 --- a/tests/menhir_tests/test10menhir/main.ml +++ b/tests/menhir_tests/test10menhir/main.ml @@ -21,8 +21,8 @@ let process (line : string) = try (* Run the parser on this line of input. *) let t = (Parser.term_start Lexer.token linebuf) in - Printf.printf " "; PPrintEngine.ToChannel.compact stdout (PP.pp_raw_term t); Printf.printf "\n"; - Printf.printf " "; PPrintEngine.ToChannel.compact stdout (PP.pp_term t); Printf.printf "\n" + Printf.printf " "; PPrint.ToChannel.compact stdout (PP.pp_raw_term t); Printf.printf "\n"; + Printf.printf " "; PPrint.ToChannel.compact stdout (PP.pp_term t); Printf.printf "\n" with | Lexer.Error msg -> Printf.fprintf stdout "%s" msg diff --git a/tests/menhir_tests/test10menhir_with_aux_args/main.ml b/tests/menhir_tests/test10menhir_with_aux_args/main.ml index e88e9e0..130dd45 100644 --- a/tests/menhir_tests/test10menhir_with_aux_args/main.ml +++ b/tests/menhir_tests/test10menhir_with_aux_args/main.ml @@ -21,8 +21,8 @@ let process (line : string) = try (* Run the parser on this line of input. *) let t = (Parser.term_start Lexer.token linebuf) in - Printf.printf " "; PPrintEngine.ToChannel.compact stdout (PP.pp_raw_term t); Printf.printf "\n"; - Printf.printf " "; PPrintEngine.ToChannel.compact stdout (PP.pp_term t); Printf.printf "\n" + Printf.printf " "; PPrint.ToChannel.compact stdout (PP.pp_raw_term t); Printf.printf "\n"; + Printf.printf " "; PPrint.ToChannel.compact stdout (PP.pp_term t); Printf.printf "\n" with | Lexer.Error msg -> Printf.fprintf stdout "%s" msg diff --git a/tests/menhir_tests/test10menhir_with_aux_rules/main.ml b/tests/menhir_tests/test10menhir_with_aux_rules/main.ml index b55a0b2..39ca044 100644 --- a/tests/menhir_tests/test10menhir_with_aux_rules/main.ml +++ b/tests/menhir_tests/test10menhir_with_aux_rules/main.ml @@ -21,9 +21,9 @@ let process (line : string) = try (* Run the parser on this line of input. *) let t = (Parser.term_start Lexer.token linebuf) in - Printf.printf " "; PPrintEngine.ToChannel.compact stdout (PP.pp_raw_term t); Printf.printf "\n"; - (* Printf.printf " "; PPrintEngine.ToChannel.compact stdout (PP.pp_term t); Printf.printf "\n"*) - Printf.printf " "; PPrintEngine.ToChannel.pretty 1.0 80 stdout (PP.pp_term t); Printf.printf "\n" + Printf.printf " "; PPrint.ToChannel.compact stdout (PP.pp_raw_term t); Printf.printf "\n"; + (* Printf.printf " "; PPrint.ToChannel.compact stdout (PP.pp_term t); Printf.printf "\n"*) + Printf.printf " "; PPrint.ToChannel.pretty 1.0 80 stdout (PP.pp_term t); Printf.printf "\n" with | Lexer.Error msg -> Printf.fprintf stdout "%s" msg diff --git a/tests/menhir_tests/test_if/main.ml b/tests/menhir_tests/test_if/main.ml index 0b6a217..ffdab99 100644 --- a/tests/menhir_tests/test_if/main.ml +++ b/tests/menhir_tests/test_if/main.ml @@ -13,8 +13,8 @@ let process (line : string) = let t = (Parser.exp_start Lexer.token linebuf) in (* and pp the original and the parsed result *) Printf.printf "ok:%s\n" line; - Printf.printf " "; PPrintEngine.ToChannel.compact stdout (PP.pp_raw_exp t); Printf.printf "\n"; - Printf.printf " "; PPrintEngine.ToChannel.compact stdout (PP.pp_exp t); Printf.printf "\n" + Printf.printf " "; PPrint.ToChannel.compact stdout (PP.pp_raw_exp t); Printf.printf "\n"; + Printf.printf " "; PPrint.ToChannel.compact stdout (PP.pp_exp t); Printf.printf "\n" with | Lexer.Error msg -> diff --git a/tests/menhir_tests/test_lists/main.ml b/tests/menhir_tests/test_lists/main.ml index 57fef21..2fd74a2 100644 --- a/tests/menhir_tests/test_lists/main.ml +++ b/tests/menhir_tests/test_lists/main.ml @@ -20,8 +20,8 @@ let process (line : string) = try (* Run the parser on this line of input. *) let t = (Parser.term_start Lexer.token linebuf) in - Printf.printf " "; PPrintEngine.ToChannel.compact stdout (PP.pp_raw_term t); Printf.printf "\n"; - Printf.printf " "; PPrintEngine.ToChannel.compact stdout (PP.pp_term t); Printf.printf "\n" + Printf.printf " "; PPrint.ToChannel.compact stdout (PP.pp_raw_term t); Printf.printf "\n"; + Printf.printf " "; PPrint.ToChannel.compact stdout (PP.pp_term t); Printf.printf "\n" with | Lexer.Error msg -> Printf.fprintf stdout "%s" msg diff --git a/tests/menhir_tests/test_phantom/main.ml b/tests/menhir_tests/test_phantom/main.ml index 8f9d50c..d802604 100644 --- a/tests/menhir_tests/test_phantom/main.ml +++ b/tests/menhir_tests/test_phantom/main.ml @@ -20,8 +20,8 @@ let process (line : string) = try (* Run the parser on this line of input. *) let t = (Parser.expr_start Lexer.token linebuf) in - Printf.printf " "; PPrintEngine.ToChannel.compact stdout (PP.pp_raw_expr t); Printf.printf "\n"; - Printf.printf " "; PPrintEngine.ToChannel.compact stdout (PP.pp_expr t); Printf.printf "\n" + Printf.printf " "; PPrint.ToChannel.compact stdout (PP.pp_raw_expr t); Printf.printf "\n"; + Printf.printf " "; PPrint.ToChannel.compact stdout (PP.pp_expr t); Printf.printf "\n" with | Lexer.Error msg -> Printf.fprintf stdout "%s" msg From fa772b37b30545424ae0f12b13635af5f750f1e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=CC=A7ois=20Pottier?= Date: Mon, 3 Jan 2022 16:00:57 +0100 Subject: [PATCH 14/46] Update the copies of PPrint bundled in tests/menhir_tests/. --- .../{PPrintCombinators.ml => PPrint.ml} | 221 +++++++++-- tests/menhir_tests/test10menhir/PPrint.mli | 345 +++++++++++++++++ .../test10menhir/PPrintCombinators.mli | 236 ------------ .../menhir_tests/test10menhir/PPrintEngine.ml | 209 ++++++++--- .../test10menhir/PPrintEngine.mli | 349 +++++++++++------- .../test10menhir/PPrintRenderer.ml | 37 -- .../PPrint.ml} | 221 +++++++++-- .../test10menhir_with_aux_args/PPrint.mli | 345 +++++++++++++++++ .../PPrintCombinators.mli | 236 ------------ .../PPrintEngine.ml | 209 ++++++++--- .../PPrintEngine.mli | 349 +++++++++++------- .../PPrintRenderer.ml | 37 -- .../PPrint.ml} | 221 +++++++++-- .../test10menhir_with_aux_rules/PPrint.mli | 345 +++++++++++++++++ .../PPrintCombinators.mli | 236 ------------ .../PPrintEngine.ml | 209 ++++++++--- .../PPrintEngine.mli | 349 +++++++++++------- .../PPrintRenderer.ml | 37 -- 18 files changed, 2724 insertions(+), 1467 deletions(-) rename tests/menhir_tests/test10menhir/{PPrintCombinators.ml => PPrint.ml} (61%) create mode 100644 tests/menhir_tests/test10menhir/PPrint.mli delete mode 100644 tests/menhir_tests/test10menhir/PPrintCombinators.mli delete mode 100644 tests/menhir_tests/test10menhir/PPrintRenderer.ml rename tests/menhir_tests/{test10menhir_with_aux_rules/PPrintCombinators.ml => test10menhir_with_aux_args/PPrint.ml} (61%) create mode 100644 tests/menhir_tests/test10menhir_with_aux_args/PPrint.mli delete mode 100644 tests/menhir_tests/test10menhir_with_aux_args/PPrintCombinators.mli delete mode 100644 tests/menhir_tests/test10menhir_with_aux_args/PPrintRenderer.ml rename tests/menhir_tests/{test10menhir_with_aux_args/PPrintCombinators.ml => test10menhir_with_aux_rules/PPrint.ml} (61%) create mode 100644 tests/menhir_tests/test10menhir_with_aux_rules/PPrint.mli delete mode 100644 tests/menhir_tests/test10menhir_with_aux_rules/PPrintCombinators.mli delete mode 100644 tests/menhir_tests/test10menhir_with_aux_rules/PPrintRenderer.ml diff --git a/tests/menhir_tests/test10menhir/PPrintCombinators.ml b/tests/menhir_tests/test10menhir/PPrint.ml similarity index 61% rename from tests/menhir_tests/test10menhir/PPrintCombinators.ml rename to tests/menhir_tests/test10menhir/PPrint.ml index 7049987..da55896 100644 --- a/tests/menhir_tests/test10menhir/PPrintCombinators.ml +++ b/tests/menhir_tests/test10menhir/PPrint.ml @@ -1,19 +1,19 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) - -open PPrintEngine - -(* ------------------------------------------------------------------------- *) +(******************************************************************************) +(* *) +(* PPrint *) +(* *) +(* François Pottier, Inria Paris *) +(* Nicolas Pouillard *) +(* *) +(* Copyright 2007-2022 Inria. All rights reserved. This file is *) +(* distributed under the terms of the GNU Library General Public *) +(* License, with an exception, as described in the file LICENSE. *) +(* *) +(******************************************************************************) + +include PPrintEngine + +(* -------------------------------------------------------------------------- *) (* Predefined single-character documents. *) @@ -31,7 +31,6 @@ let bquote = char '`' let semi = char ';' let colon = char ':' let comma = char ',' -let space = char ' ' let dot = char '.' let sharp = char '#' let slash = char '/' @@ -51,7 +50,7 @@ let underscore = char '_' let bang = char '!' let bar = char '|' -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Repetition. *) @@ -67,7 +66,7 @@ let repeat n doc = in loop n doc empty -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Delimiters. *) @@ -83,7 +82,7 @@ let parens = enclose lparen rparen let angles = enclose langle rangle let brackets = enclose lbracket rbracket -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Some functions on lists. *) @@ -97,7 +96,7 @@ let foldli (f : int -> 'b -> 'a -> 'b) (accu : 'b) (xs : 'a list) : 'b = f i accu x ) accu xs -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Working with lists of documents. *) @@ -147,7 +146,7 @@ let optional f = function | Some x -> f x -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Text. *) @@ -210,7 +209,7 @@ let words s = let n = String.length s in (* A two-state finite automaton. *) (* In this state, we have skipped at least one blank character. *) - let rec skipping accu i = + let rec skipping accu i = if i = n then (* There was whitespace at the end. Drop it. *) accu @@ -235,7 +234,7 @@ let words s = | '\n' | '\r' -> (* A new word has been identified. *) - let accu = substring s i (j - i) :: accu in + let accu = substring s i (j - i) :: accu in skipping accu (j + 1) | _ -> (* Continue inside the current word. *) @@ -252,7 +251,7 @@ let flow_map sep f docs = (* This idiom allows beginning a new line if [doc] does not fit on the current line. *) group (sep ^^ f doc) - ) empty docs + ) empty docs let flow sep docs = flow_map sep (fun x -> x) docs @@ -260,8 +259,7 @@ let flow sep docs = let url s = flow (break 0) (split (function '/' | '.' -> true | _ -> false) s) -(* ------------------------------------------------------------------------- *) - +(* -------------------------------------------------------------------------- *) (* Alignment and indentation. *) let hang i d = @@ -281,11 +279,6 @@ let (^//^) = let jump n b y = group (nest n (break b ^^ y)) -(* Deprecated. -let ( ^@^ ) x y = group (x ^/^ y) -let ( ^@@^ ) x y = group (nest 2 (x ^/^ y)) -*) - let infix n b op x y = prefix n b (x ^^ blank b ^^ op) y @@ -309,3 +302,167 @@ let surround_separate_map n b void opening sep closing f xs = | _ :: _ -> surround n b opening (separate_map sep f xs) closing +(* -------------------------------------------------------------------------- *) +(* Printing OCaml values. *) + +module OCaml = struct + +open Printf + +type constructor = string +type type_name = string +type record_field = string +type tag = int + +(* -------------------------------------------------------------------------- *) + +(* This internal [sprintf]-like function produces a document. We use [string], + as opposed to [arbitrary_string], because the strings that we produce will + never contain a newline character. *) + +let dsprintf format = + ksprintf string format + +(* -------------------------------------------------------------------------- *) + +(* Nicolas prefers using this code as opposed to just [sprintf "%g"] or + [sprintf "%f"]. The latter print [inf] and [-inf], whereas OCaml + understands [infinity] and [neg_infinity]. [sprintf "%g"] does not add a + trailing dot when the number happens to be an integral number. [sprintf + "%F"] seems to lose precision and ignores the precision modifier. *) + +let valid_float_lexeme (s : string) : string = + let l = String.length s in + let rec loop i = + if i >= l then + (* If we reach the end of the string and have found only characters in + the set '0' .. '9' and '-', then this string will be considered as an + integer literal by OCaml. Adding a trailing dot makes it a float + literal. *) + s ^ "." + else + match s.[i] with + | '0' .. '9' | '-' -> loop (i + 1) + | _ -> s + in loop 0 + +(* This function constructs a string representation of a floating point + number. This representation is supposed to be accepted by OCaml as a + valid floating point literal. *) + +let float_representation (f : float) : string = + match classify_float f with + | FP_nan -> + "nan" + | FP_infinite -> + if f < 0.0 then "neg_infinity" else "infinity" + | _ -> + (* Try increasing precisions and validate. *) + let s = sprintf "%.12g" f in + if f = float_of_string s then valid_float_lexeme s else + let s = sprintf "%.15g" f in + if f = float_of_string s then valid_float_lexeme s else + sprintf "%.18g" f + +(* -------------------------------------------------------------------------- *) + +(* A few constants and combinators, used below. *) + +let some = + string "Some" + +let none = + string "None" + +let lbracketbar = + string "[|" + +let rbracketbar = + string "|]" + +let seq1 opening separator closing = + surround_separate 2 0 + (opening ^^ closing) opening (separator ^^ break 1) closing + +let seq2 opening separator closing = + surround_separate_map 2 1 + (opening ^^ closing) opening (separator ^^ break 1) closing + +(* -------------------------------------------------------------------------- *) + +(* The following functions are printers for many types of OCaml values. *) + +(* There is no protection against cyclic values. *) + +let tuple = + seq1 lparen comma rparen + +let variant _ cons _ args = + match args with + | [] -> + !^cons + | _ :: _ -> + !^cons ^^ tuple args + +let record _ fields = + seq2 lbrace semi rbrace (fun (k, v) -> infix 2 1 equals !^k v) fields + +let option f = function + | None -> + none + | Some x -> + some ^^ tuple [f x] + +let list f xs = + seq2 lbracket semi rbracket f xs + +let flowing_list f xs = + group (lbracket ^^ space ^^ nest 2 ( + flow_map (semi ^^ break 1) f xs + ) ^^ space ^^ rbracket) + +let array f xs = + seq2 lbracketbar semi rbracketbar f (Array.to_list xs) + +let flowing_array f xs = + group (lbracketbar ^^ space ^^ nest 2 ( + flow_map (semi ^^ break 1) f (Array.to_list xs) + ) ^^ space ^^ rbracketbar) + +let ref f x = + record "ref" ["contents", f !x] + +let float f = + string (float_representation f) + +let int = + dsprintf "%d" + +let int32 = + dsprintf "%ld" + +let int64 = + dsprintf "%Ld" + +let nativeint = + dsprintf "%nd" + +let char = + dsprintf "%C" + +let bool = + dsprintf "%B" + +let unit = + dsprintf "()" + +let string = + dsprintf "%S" + +let unknown tyname _ = + dsprintf "" tyname + +type representation = + document + +end (* OCaml *) diff --git a/tests/menhir_tests/test10menhir/PPrint.mli b/tests/menhir_tests/test10menhir/PPrint.mli new file mode 100644 index 0000000..89eeeeb --- /dev/null +++ b/tests/menhir_tests/test10menhir/PPrint.mli @@ -0,0 +1,345 @@ +(******************************************************************************) +(* *) +(* PPrint *) +(* *) +(* François Pottier, Inria Paris *) +(* Nicolas Pouillard *) +(* *) +(* Copyright 2007-2022 Inria. All rights reserved. This file is *) +(* distributed under the terms of the GNU Library General Public *) +(* License, with an exception, as described in the file LICENSE. *) +(* *) +(******************************************************************************) + +include module type of PPrintEngine (** @inline *) + +(** {1:combinators High-Level Combinators} *) + +(** {2 Single Characters} *) + +(**The following atomic documents consist of a single character. Each of + them is a synonym for the application of {!char} to some constant + character. For instance, {!lparen} is a synonym for [char '(']. *) + +val lparen: document +val rparen: document +val langle: document +val rangle: document +val lbrace: document +val rbrace: document +val lbracket: document +val rbracket: document +val squote: document +val dquote: document +val bquote: document +val semi: document +val colon: document +val comma: document +val dot: document +val sharp: document +val slash: document +val backslash: document +val equals: document +val qmark: document +val tilde: document +val at: document +val percent: document +val dollar: document +val caret: document +val ampersand: document +val star: document +val plus: document +val minus: document +val underscore: document +val bang: document +val bar: document + +(** {2 Delimiters} *) + +(**[precede l x] is [l ^^ x]. *) +val precede: document -> document -> document + +(**[terminate r x] is [x ^^ r]. *) +val terminate: document -> document -> document + +(**[enclose l r x] is [l ^^ x ^^ r]. *) +val enclose: document -> document -> document -> document + +(**The following combinators enclose a document within a pair of delimiters. + They are partial applications of [enclose]. No whitespace or line break + is introduced. *) + +val squotes: document -> document +val dquotes: document -> document +val bquotes: document -> document +val braces: document -> document +val parens: document -> document +val angles: document -> document +val brackets: document -> document + +(** {2 Repetition} *) + +(**[twice doc] is the document obtained by concatenating two copies of + the document [doc]. *) +val twice: document -> document + +(**[repeat n doc] is the document obtained by concatenating [n] copies of + the document [doc]. *) +val repeat: int -> document -> document + +(** {2 Lists and Options} *) + +(**[concat docs] is the concatenation of the documents in the list [docs]. *) +val concat: document list -> document + +(**[separate sep docs] is the concatenation of the documents in the list + [docs]. The separator [sep] is inserted between every two adjacent + documents. *) +val separate: document -> document list -> document + +(**[concat_map f xs] is equivalent to [concat (List.map f xs)]. *) +val concat_map: ('a -> document) -> 'a list -> document + +(**[separate_map sep f xs] is equivalent to [separate sep (List.map f xs)]. *) +val separate_map: document -> ('a -> document) -> 'a list -> document + +(**[separate2 sep last_sep docs] is the concatenation of the documents in + the list [docs]. The separator [sep] is inserted between every two + adjacent documents, except between the last two documents, where the + separator [last_sep] is used instead. *) +val separate2: document -> document -> document list -> document + +(**[optional f None] is the empty document. [optional f (Some x)] is + the document [f x]. *) +val optional: ('a -> document) -> 'a option -> document + +(** {2 Text} *) + +(**[lines s] is the list of documents obtained by splitting [s] at newline + characters, and turning each line into a document via [substring]. This + code is not UTF-8 aware. *) +val lines: string -> document list + +(**[arbitrary_string s] is equivalent to [separate (break 1) (lines s)]. + It is analogous to [string s], but is valid even if the string [s] + contains newline characters. *) +val arbitrary_string: string -> document + +(**[words s] is the list of documents obtained by splitting [s] at whitespace + characters, and turning each word into a document via [substring]. All + whitespace is discarded. This code is not UTF-8 aware. *) +val words: string -> document list + +(**[split ok s] splits the string [s] before and after every occurrence of a + character that satisfies the predicate [ok]. The substrings thus obtained + are turned into documents, and a list of documents is returned. No + information is lost: the concatenation of the documents yields the + original string. This code is not UTF-8 aware. *) +val split: (char -> bool) -> string -> document list + +(**[flow sep docs] separates the documents in the list [docs] with the + separator [sep] and arranges for a new line to begin whenever a document + does not fit on the current line. This is useful for typesetting + free-flowing, ragged-right text. A typical choice of [sep] is [break b], + where [b] is the number of spaces that must be inserted between two + consecutive words (when displayed on the same line). *) +val flow: document -> document list -> document + +(**[flow_map sep f docs] is equivalent to [flow sep (List.map f docs)]. *) +val flow_map: document -> ('a -> document) -> 'a list -> document + +(**[url s] is a possible way of displaying the URL [s]. A potential line + break is inserted immediately before and immediately after every slash + and dot character. *) +val url: string -> document + +(** {2 Alignment and Indentation} *) + +(**[hang n doc] is analogous to [align], but additionally indents all lines, + except the first one, by [n]. Thus, the text in the box forms a hanging + indent. *) +val hang: int -> document -> document + +(**[prefix n b left right] has the following flat layout: + {[ + left right + ]} + and the following non-flat layout: + {[ + left + right + ]} + The parameter [n] controls the nesting of [right] (when not flat). + The parameter [b] controls the number of spaces between [left] and [right] + (when flat). *) +val prefix: int -> int -> document -> document -> document + +(**[jump n b right] is equivalent to [prefix n b empty right]. *) +val jump: int -> int -> document -> document + +(**[infix n b middle left right] has the following flat layout: + {[ + left middle right + ]} + and the following non-flat layout: + {[ + left middle + right + ]} + The parameter [n] controls the nesting of [right] (when not flat). + The parameter [b] controls the number of spaces between [left] and [middle] + (always) and between [middle] and [right] (when flat). *) +val infix: int -> int -> document -> document -> document -> document + +(**[surround n b opening contents closing] has the following flat layout: + {[ + opening contents closing + ]} + and the following non-flat layout: + {[ + opening + contents + closing + ]} + The parameter [n] controls the nesting of [contents] (when not flat). + The parameter [b] controls the number of spaces between [opening] and + [contents] and between [contents] and [closing] (when flat). +*) +val surround: int -> int -> document -> document -> document -> document + +(**[soft_surround] is analogous to [surround], but involves more than one + group, so it offers possibilities other than the completely flat layout + (where [opening], [contents], and [closing] appear on a single line) and + the completely developed layout (where [opening], [contents], and + [closing] appear on separate lines). It tries to place the beginning of + [contents] on the same line as [opening], and to place [closing] on the + same line as the end of [contents], if possible. *) +val soft_surround: int -> int -> document -> document -> document -> document + +(**[surround_separate n b void opening sep closing docs] is equivalent to + [surround n b opening (separate sep docs) closing], except when the list + [docs] is empty, in which case it reduces to [void]. *) +val surround_separate: + int -> int -> + document -> document -> document -> document -> + document list -> document + +(**[surround_separate_map n b void opening sep closing f xs] is equivalent + to [surround_separate n b void opening sep closing (List.map f xs)]. *) +val surround_separate_map: + int -> int -> + document -> document -> document -> document -> + ('a -> document) -> 'a list -> document + +(** {2 Short-Hands} *) + +(**[!^s] is a short-hand for [string s]. *) +val ( !^ ) : string -> document + +(**[x ^/^ y] separates [x] and [y] with a breakable space. + It is a short-hand for [x ^^ break 1 ^^ y]. *) +val ( ^/^ ) : document -> document -> document + +(**[x ^//^ y] is a short-hand for [prefix 2 1 x y]. *) +val ( ^//^ ) : document -> document -> document + +(** {1:ocaml Printing OCaml Values} *) + +(**This module offers document combinators that help print OCaml values. The + strings produced by rendering these documents are supposed to be accepted + by the OCaml parser as valid values. + + These functions do {i not} distinguish between mutable and immutable + values. They do {i not} recognize sharing, and do {i not} incorporate a + protection against cyclic values. *) +module OCaml : sig + +(* The signature of this module is compatible with that expected by the + [camlp4] generator [Camlp4RepresentationGenerator]. This explains why + some functions have unused parameters. This is also the reason why + there is a type [representation]. *) + +type constructor = string +type type_name = string +type record_field = string +type tag = int + +(**[variant _ dc _ args] represents a constructed value whose data + constructor is [dc] and whose arguments are [args]. The other two + parameters are presently unused. *) +val variant : type_name -> constructor -> tag -> document list -> document + +(**[record _ fields] represents a record value whose fields are [fields]. + The other parameter is presently unused. *) +val record : type_name -> (record_field * document) list -> document + +(**[tuple args] represents a tuple value whose components are [args]. *) +val tuple : document list -> document + +(**[string s] represents the literal string [s]. *) +val string : string -> document + +(**[int i] represents the literal integer [i]. *) +val int : int -> document + +(**[int32 i] represents the literal 32-bit integer [i]. *) +val int32 : int32 -> document + +(**[int64 i] represents the literal 64-bit integer [i]. *) +val int64 : int64 -> document + +(**[nativeint i] represents the literal native integer [i]. *) +val nativeint : nativeint -> document + +(**[float f] represents the literal floating-point number [f]. *) +val float : float -> document + +(**[char c] represents the literal character [c]. *) +val char : char -> document + +(**[bool b] represents the Boolean value [b]. *) +val bool : bool -> document + +(**[unit] represents the unit constant [()]. *) +val unit : document + +(**[option f o] represents the option [o]. The representation of the + element, if present, is computed by the function [f]. *) +val option : ('a -> document) -> 'a option -> document + +(**[list f xs] represents the list [xs]. The representation of each element + is computed by the function [f]. If the whole list fits on a single line, + then it is printed on a single line; otherwise each element is printed on + a separate line. *) +val list : ('a -> document) -> 'a list -> document + +(**[flowing_list f xs] represents the list [xs]. The representation of each + element is computed by the function [f]. As many elements are possible + are printed on each line. *) +val flowing_list : ('a -> document) -> 'a list -> document + +(**[array f xs] represents the array [xs]. The representation of each + element is computed by the function [f]. If the whole array fits on a + single line, then it is printed on a single line; otherwise each element + is printed on a separate line. *) +val array : ('a -> document) -> 'a array -> document + +(**[flowing_array f xs] represents the array [xs]. The representation of + each element is computed by the function [f]. As many elements are + possible are printed on each line. *) +val flowing_array : ('a -> document) -> 'a array -> document + +(**[ref r] represents the reference [r]. The representation of the content + is computed by the function [f]. *) +val ref : ('a -> document) -> 'a ref -> document + +(** [unknown t _] represents an unknown value of type [t]. It is rendered + as a string of the form []. *) +val unknown : type_name -> 'a -> document + +(**/**) + +type representation = + document + +end (* OCaml *) diff --git a/tests/menhir_tests/test10menhir/PPrintCombinators.mli b/tests/menhir_tests/test10menhir/PPrintCombinators.mli deleted file mode 100644 index c538cb3..0000000 --- a/tests/menhir_tests/test10menhir/PPrintCombinators.mli +++ /dev/null @@ -1,236 +0,0 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) - -open PPrintEngine - -(** A set of high-level combinators for building documents. *) - -(** {1 Single characters} *) - -(** The following constant documents consist of a single character. *) - -val lparen: document -val rparen: document -val langle: document -val rangle: document -val lbrace: document -val rbrace: document -val lbracket: document -val rbracket: document -val squote: document -val dquote: document -val bquote: document -val semi: document -val colon: document -val comma: document -val space: document -val dot: document -val sharp: document -val slash: document -val backslash: document -val equals: document -val qmark: document -val tilde: document -val at: document -val percent: document -val dollar: document -val caret: document -val ampersand: document -val star: document -val plus: document -val minus: document -val underscore: document -val bang: document -val bar: document - -(** {1 Delimiters} *) - -(** [precede l x] is [l ^^ x]. *) -val precede: document -> document -> document - -(** [terminate r x] is [x ^^ r]. *) -val terminate: document -> document -> document - -(** [enclose l r x] is [l ^^ x ^^ r]. *) -val enclose: document -> document -> document -> document - -(** The following combinators enclose a document within a pair of delimiters. - They are partial applications of [enclose]. No whitespace or line break is - introduced. *) - -val squotes: document -> document -val dquotes: document -> document -val bquotes: document -> document -val braces: document -> document -val parens: document -> document -val angles: document -> document -val brackets: document -> document - -(** {1 Repetition} *) - -(** [twice doc] is the document obtained by concatenating two copies of - the document [doc]. *) -val twice: document -> document - -(** [repeat n doc] is the document obtained by concatenating [n] copies of - the document [doc]. *) -val repeat: int -> document -> document - -(** {1 Lists and options} *) - -(** [concat docs] is the concatenation of the documents in the list [docs]. *) -val concat: document list -> document - -(** [separate sep docs] is the concatenation of the documents in the list - [docs]. The separator [sep] is inserted between every two adjacent - documents. *) -val separate: document -> document list -> document - -(** [concat_map f xs] is equivalent to [concat (List.map f xs)]. *) -val concat_map: ('a -> document) -> 'a list -> document - -(** [separate_map sep f xs] is equivalent to [separate sep (List.map f xs)]. *) -val separate_map: document -> ('a -> document) -> 'a list -> document - -(** [separate2 sep last_sep docs] is the concatenation of the documents in the - list [docs]. The separator [sep] is inserted between every two adjacent - documents, except between the last two documents, where the separator - [last_sep] is used instead. *) -val separate2: document -> document -> document list -> document - -(** [optional f None] is the empty document. [optional f (Some x)] is - the document [f x]. *) -val optional: ('a -> document) -> 'a option -> document - -(** {1 Text} *) - -(** [lines s] is the list of documents obtained by splitting [s] at newline - characters, and turning each line into a document via [substring]. This - code is not UTF-8 aware. *) -val lines: string -> document list - -(** [arbitrary_string s] is equivalent to [separate (break 1) (lines s)]. - It is analogous to [string s], but is valid even if the string [s] - contains newline characters. *) -val arbitrary_string: string -> document - -(** [words s] is the list of documents obtained by splitting [s] at whitespace - characters, and turning each word into a document via [substring]. All - whitespace is discarded. This code is not UTF-8 aware. *) -val words: string -> document list - -(** [split ok s] splits the string [s] before and after every occurrence of a - character that satisfies the predicate [ok]. The substrings thus obtained - are turned into documents, and a list of documents is returned. No - information is lost: the concatenation of the documents yields the - original string. This code is not UTF-8 aware. *) -val split: (char -> bool) -> string -> document list - -(** [flow sep docs] separates the documents in the list [docs] with the - separator [sep] and arranges for a new line to begin whenever a document - does not fit on the current line. This is useful for typesetting - free-flowing, ragged-right text. A typical choice of [sep] is [break b], - where [b] is the number of spaces that must be inserted between two - consecutive words (when displayed on the same line). *) -val flow: document -> document list -> document - -(** [flow_map sep f docs] is equivalent to [flow sep (List.map f docs)]. *) -val flow_map: document -> ('a -> document) -> 'a list -> document - -(** [url s] is a possible way of displaying the URL [s]. A potential line - break is inserted immediately before and immediately after every slash - and dot character. *) -val url: string -> document - -(** {1 Alignment and indentation} *) - -(* [hang n doc] is analogous to [align], but additionally indents - all lines, except the first one, by [n]. Thus, the text in the - box forms a hanging indent. *) -val hang: int -> document -> document - -(** [prefix n b left right] has the following flat layout: {[ -left right -]} -and the following non-flat layout: -{[ -left - right -]} -The parameter [n] controls the nesting of [right] (when not flat). -The parameter [b] controls the number of spaces between [left] and [right] -(when flat). - *) -val prefix: int -> int -> document -> document -> document - -(** [jump n b right] is equivalent to [prefix n b empty right]. *) -val jump: int -> int -> document -> document - -(** [infix n b middle left right] has the following flat layout: {[ -left middle right -]} -and the following non-flat layout: {[ -left middle - right -]} -The parameter [n] controls the nesting of [right] (when not flat). -The parameter [b] controls the number of spaces between [left] and [middle] -(always) and between [middle] and [right] (when flat). -*) -val infix: int -> int -> document -> document -> document -> document - -(** [surround n b opening contents closing] has the following flat layout: {[ -opening contents closing -]} -and the following non-flat layout: {[ -opening - contents -closing -]} -The parameter [n] controls the nesting of [contents] (when not flat). -The parameter [b] controls the number of spaces between [opening] and [contents] -and between [contents] and [closing] (when flat). -*) -val surround: int -> int -> document -> document -> document -> document - -(** [soft_surround] is analogous to [surround], but involves more than one - group, so it offers possibilities other than the completely flat layout - (where [opening], [contents], and [closing] appear on a single line) and - the completely developed layout (where [opening], [contents], and - [closing] appear on separate lines). It tries to place the beginning of - [contents] on the same line as [opening], and to place [closing] on the - same line as the end of [contents], if possible. -*) -val soft_surround: int -> int -> document -> document -> document -> document - -(** [surround_separate n b void opening sep closing docs] is equivalent to - [surround n b opening (separate sep docs) closing], except when the - list [docs] is empty, in which case it reduces to [void]. *) -val surround_separate: int -> int -> document -> document -> document -> document -> document list -> document - -(** [surround_separate_map n b void opening sep closing f xs] is equivalent to - [surround_separate n b void opening sep closing (List.map f xs)]. *) -val surround_separate_map: int -> int -> document -> document -> document -> document -> ('a -> document) -> 'a list -> document - -(** {1 Short-hands} *) - -(** [!^s] is a short-hand for [string s]. *) -val ( !^ ) : string -> document - -(** [x ^/^ y] separates [x] and [y] with a breakable space. - It is a short-hand for [x ^^ break 1 ^^ y]. *) -val ( ^/^ ) : document -> document -> document - -(** [x ^//^ y] is a short-hand for [prefix 2 1 x y]. *) -val ( ^//^ ) : document -> document -> document - diff --git a/tests/menhir_tests/test10menhir/PPrintEngine.ml b/tests/menhir_tests/test10menhir/PPrintEngine.ml index 2a78363..3131893 100644 --- a/tests/menhir_tests/test10menhir/PPrintEngine.ml +++ b/tests/menhir_tests/test10menhir/PPrintEngine.ml @@ -1,15 +1,23 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) +(******************************************************************************) +(* *) +(* PPrint *) +(* *) +(* François Pottier, Inria Paris *) +(* Nicolas Pouillard *) +(* *) +(* Copyright 2007-2022 Inria. All rights reserved. This file is *) +(* distributed under the terms of the GNU Library General Public *) +(* License, with an exception, as described in the file LICENSE. *) +(* *) +(******************************************************************************) + +(** A point is a pair of a line number and a column number. *) +type point = + int * int + +(** A range is a pair of points. *) +type range = + point * point (* ------------------------------------------------------------------------- *) @@ -51,6 +59,82 @@ class type output = object end +(* ------------------------------------------------------------------------- *) + +(* Printing blank space. This is used both internally (to emit indentation + characters) and via the public combinator [blank]. *) + +let blank_length = + 80 + +let blank_buffer = + String.make blank_length ' ' + +let rec blanks (output : output) n = + if n <= 0 then + () + else if n <= blank_length then + output#substring blank_buffer 0 n + else begin + output#substring blank_buffer 0 blank_length; + blanks output (n - blank_length) + end + +(* ------------------------------------------------------------------------- *) + +(* The class [buffering] implements a wrapper that delays the printing of + blank characters. This includes indentation characters and characters + produced by the combinator [blank]. The printing of these characters is + delayed until it is known that they are followed by something on the same + line; if they are not followed with anything, then it is canceled. + + The actual printing task is delegated to the object [delegate], whose type + is [output]; the new object has type [output] as well. *) + +class buffering (delegate : output) : output = object (self) + + (* The number of blank characters that are withholding. *) + val mutable buffered = 0 + + (* [flush] sends out the blank characters that have been withheld. *) + method private flush = + blanks delegate buffered; + buffered <- 0 + + method char c : unit = + begin match c with + | '\n' -> + (* The current line ends here. Any blank characters that were withheld + are destroyed. This is where we avoid printing blank characters if + nothing follows them. *) + buffered <- 0 + | _ -> + (* The current line is nonempty. Any blank characters that were + withheld can now be flushed. *) + self#flush + end; + (* Print this character as usual. *) + delegate#char c + + method substring s pos len = + (* If this is a string of length zero, then there is nothing to do. *) + if len = 0 then + () + (* If this is a blank string (which we recognize by its address), then + its content is withheld. *) + else if s == blank_buffer then + buffered <- buffered + len + (* If this is not a blank string, then the blank characters that were + withheld up to this point can now be flushed. *) + else begin + self#flush; + delegate#substring s pos len + end + +end + +(* ------------------------------------------------------------------------- *) + (* Three kinds of output channels are wrapped so as to satisfy the above interface: OCaml output channels, OCaml memory buffers, and OCaml formatters. *) @@ -72,8 +156,17 @@ class buffer_output buffer = object end class formatter_output fmt = object - method char = Format.pp_print_char fmt - method substring = fst (Format.pp_get_formatter_output_functions fmt ()) + method char = function + | '\n' -> Format.pp_force_newline fmt () + | ' ' -> Format.pp_print_space fmt () + | c -> Format.pp_print_char fmt c + + method substring str ofs len = + Format.pp_print_text fmt ( + if ofs = 0 && len = String.length str + then str + else String.sub str ofs len + ) end (* ------------------------------------------------------------------------- *) @@ -94,10 +187,14 @@ type state = { mutable last_indent: int; (** The number of blanks that were printed at the beginning of the current - line. This field is updated (only) by the function [emit_hardline]. It - is used (only) to determine whether the ribbon width constraint is + line. This field is updated (only) when a hardline is emitted. It is + used (only) to determine whether the ribbon width constraint is respected. *) + mutable line: int; + (** The current line. This field is updated (only) when a hardline is + emitted. It is not used by the pretty-printing engine itself. *) + mutable column: int; (** The current column. This field must be updated whenever something is sent to the output channel. It is used (only) to determine whether the @@ -113,6 +210,7 @@ let initial rfrac width = { width = width; ribbon = max 0 (min width (truncate (float_of_int width *. rfrac))); last_indent = 0; + line = 0; column = 0 } @@ -245,6 +343,12 @@ type document = | Align of requirement * document + (* [Range (req, hook, doc)] is printed like [doc]. After it is printed, the + function [hook] is applied to the range that is occupied by [doc] in the + output. *) + + | Range of requirement * (range -> unit) * document + (* [Custom (req, f)] is a document whose appearance is user-defined. *) | Custom of custom @@ -276,7 +380,8 @@ let rec requirement = function | Cat (req, _, _) | Nest (req, _, _) | Group (req, _) - | Align (req, _) -> + | Align (req, _) + | Range (req, _, _) -> (* These nodes store their requirement -- which is computed when the node is constructed -- so as to allow us to answer in constant time here. *) @@ -298,7 +403,7 @@ let char c = Char c let space = - char ' ' + Blank 1 let string s = String s @@ -332,6 +437,9 @@ let utf8_length s = let utf8string s = fancystring s (utf8_length s) +let utf8format f = + Printf.ksprintf utf8string f + let hardline = HardLine @@ -339,8 +447,6 @@ let blank n = match n with | 0 -> empty - | 1 -> - space | _ -> Blank n @@ -394,6 +500,9 @@ let group x = let align x = Align (requirement x, x) +let range hook x = + Range (requirement x, hook, x) + let custom c = (* Sanity check. *) assert (c#requirement >= 0); @@ -401,26 +510,6 @@ let custom c = (* ------------------------------------------------------------------------- *) -(* Printing blank space (indentation characters). *) - -let blank_length = - 80 - -let blank_buffer = - String.make blank_length ' ' - -let rec blanks output n = - if n <= 0 then - () - else if n <= blank_length then - output#substring blank_buffer 0 n - else begin - output#substring blank_buffer 0 blank_length; - blanks output (n - blank_length) - end - -(* ------------------------------------------------------------------------- *) - (* This function expresses the following invariant: if we are in flattening mode, then we must be within bounds, i.e. the width and ribbon width constraints must be respected. *) @@ -445,12 +534,14 @@ let ok state flatten : bool = manner in which the document is rendered. *) (* The code is written in tail-recursive style, so as to avoid running out of - stack space if the document is very deep. Its explicit continuation can be - viewed as a sequence of pending calls to [pretty]. *) + stack space if the document is very deep. Each [KCons] cell in a + continuation represents a pending call to [pretty]. Each [KRange] cell + represents a pending call to a user-provided range hook. *) type cont = | KNil | KCons of int * bool * document * cont + | KRange of (range -> unit) * point * cont let rec pretty (output : output) @@ -498,6 +589,7 @@ let rec pretty (* Emit a hardline. *) output#char '\n'; blanks output indent; + state.line <- state.line + 1; state.column <- indent; state.last_indent <- indent; (* Continue. *) @@ -537,6 +629,10 @@ let rec pretty (* assert (state.column > state.last_indent); *) pretty output state state.column flatten doc cont + | Range (_, hook, doc) -> + let start : point = (state.line, state.column) in + pretty output state indent flatten doc (KRange (hook, start, cont)) + | Custom c -> (* Invoke the document's custom rendering function. *) c#pretty output state indent flatten; @@ -550,6 +646,10 @@ and continue output state = function () | KCons (indent, flatten, doc, cont) -> pretty output state indent flatten doc cont + | KRange (hook, start, cont) -> + let finish : point = (state.line, state.column) in + hook (start, finish); + continue output state cont (* Publish a version of [pretty] that does not take an explicit continuation. This function may be used by authors of custom documents. We do not expose @@ -575,7 +675,7 @@ let rec compact output doc cont = let len = String.length s in output#substring s 0 len; continue output cont - | FancyString (s, ofs, len, apparent_length) -> + | FancyString (s, ofs, len, _apparent_length) -> output#substring s ofs len; continue output cont | Blank n -> @@ -589,7 +689,8 @@ let rec compact output doc cont = | IfFlat (doc, _) | Nest (_, _, doc) | Group (_, doc) - | Align (_, doc) -> + | Align (_, doc) + | Range (_, _, doc) -> compact output doc cont | Custom c -> (* Invoke the document's custom rendering function. *) @@ -612,13 +713,21 @@ let compact output doc = (* This is just boilerplate. *) +module type RENDERER = sig + type channel + type document + val pretty: float -> int -> channel -> document -> unit + val compact: channel -> document -> unit +end + module MakeRenderer (X : sig type channel val output: channel -> output -end) = struct +end) +: RENDERER with type channel = X.channel and type document = document += struct type channel = X.channel - type dummy = document - type document = dummy + type nonrec document = document let pretty rfrac width channel doc = pretty (X.output channel) (initial rfrac width) 0 false doc let compact channel doc = compact (X.output channel) doc end @@ -626,17 +735,17 @@ end module ToChannel = MakeRenderer(struct type channel = out_channel - let output = new channel_output + let output channel = new buffering (new channel_output channel) end) module ToBuffer = MakeRenderer(struct type channel = Buffer.t - let output = new buffer_output + let output buffer = new buffering (new buffer_output buffer) end) module ToFormatter = MakeRenderer(struct type channel = Format.formatter - let output = new formatter_output + let output fmt = new buffering (new formatter_output fmt) end) diff --git a/tests/menhir_tests/test10menhir/PPrintEngine.mli b/tests/menhir_tests/test10menhir/PPrintEngine.mli index eda61a6..4d03b12 100644 --- a/tests/menhir_tests/test10menhir/PPrintEngine.mli +++ b/tests/menhir_tests/test10menhir/PPrintEngine.mli @@ -1,150 +1,226 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) - -(** A pretty-printing engine and a set of basic document combinators. *) - -(** {1 Building documents} *) - -(** Documents must be built in memory before they are rendered. This may seem - costly, but it is a simple approach, and works well. *) - -(** The following operations form a set of basic (low-level) combinators for - building documents. On top of these combinators, higher-level combinators - can be defined: see {!PPrintCombinators}. *) - -(** This is the abstract type of documents. *) +(******************************************************************************) +(* *) +(* PPrint *) +(* *) +(* François Pottier, Inria Paris *) +(* Nicolas Pouillard *) +(* *) +(* Copyright 2007-2022 Inria. All rights reserved. This file is *) +(* distributed under the terms of the GNU Library General Public *) +(* License, with an exception, as described in the file LICENSE. *) +(* *) +(******************************************************************************) + +(**[PPrint] is an OCaml library for {b pretty-printing textual documents}. + It takes care of {b indentation and line breaks}, and is typically used + to {b pretty-print code}. *) + +(** {1:building Building Documents} *) + +(**The abstract type of documents. *) type document -(** The following basic (low-level) combinators allow constructing documents. *) +(** {2 Atomic Documents} *) -(** [empty] is the empty document. *) +(**[empty] is the empty document. *) val empty: document -(** [char c] is a document that consists of the single character [c]. This - character must not be a newline. *) +(**[char c] is an atomic document that consists of the single character [c]. + This character must not be a newline character. *) val char: char -> document -(** [string s] is a document that consists of the string [s]. This string must - not contain a newline. *) +(**[string s] is an atomic document that consists of the string [s]. This + string must not contain a newline. The printing engine assumes that the + ideal width of this string is [String.length s]. This assumption is safe + if this is an ASCII string. Otherwise, {!fancystring} or {!utf8string} + should be preferred. *) val string: string -> document -(** [substring s ofs len] is a document that consists of the portion of the - string [s] delimited by the offset [ofs] and the length [len]. This - portion must not contain a newline. *) +(**[substring s ofs len] is an atomic document that consists of the portion + of the string [s] delimited by the offset [ofs] and the length [len]. + This portion must not contain a newline. [substring s ofs len] is + equivalent to [string (String.sub s ofs len)], but is expected to be more + efficient, as the substring is not actually extracted. *) val substring: string -> int -> int -> document -(** [fancystring s apparent_length] is a document that consists of the string - [s]. This string must not contain a newline. The string may contain fancy - characters: color escape characters, UTF-8 or multi-byte characters, - etc. Thus, its apparent length (which measures how many columns the text - will take up on screen) differs from its length in bytes. *) +(**[fancystring s alen] is an atomic document that consists of the string + [s]. This string must not contain a newline. The string may contain fancy + characters: color escape characters, UTF-8 characters, etc. Thus, its + apparent length (which measures how many columns the text will take up on + screen) differs from its length in bytes. The printing engine assumes + that its apparent length is [alen]. *) val fancystring: string -> int -> document -(** [fancysubstring s ofs len apparent_length] is a document that consists of - the portion of the string [s] delimited by the offset [ofs] and the length - [len]. This portion must contain a newline. The string may contain fancy - characters. *) +(**[fancysubstring s ofs len alen] is equivalent to [fancystring (String.sub + s ofs len) alen]. *) val fancysubstring : string -> int -> int -> int -> document -(** [utf8string s] is a document that consists of the UTF-8-encoded string [s]. - This string must not contain a newline. *) +(**[utf8string s] is an atomic document that consists of the UTF-8-encoded + string [s]. This string must not contain a newline. [utf8string s] is + equivalent to [fancystring s (utf8_length s)], where [utf8_length s] is + the apparent length of the UTF-8-encoded string [s]. *) val utf8string: string -> document -(** [hardline] is a forced newline document. This document forces all enclosing - groups to be printed in non-flattening mode. In other words, any enclosing - groups are dissolved. *) +(** [utf8format format ...] is equivalent to + [utf8string (Printf.sprintf format ...)]. *) +val utf8format: ('a, unit, string, document) format4 -> 'a + +(** {2 Blanks and Newlines} *) + +(**The atomic document [hardline] represents a forced newline. This document + has infinite ideal width: thus, if there is a choice between printing it + in flat mode and printing it in normal mode, normal mode is preferred. In + other words, when [hardline] is placed directly inside a group, this + group is dissolved: [group hardline] is equivalent to [hardline]. This + combinator should be seldom used; consider using {!break} instead. *) val hardline: document -(** [blank n] is a document that consists of [n] blank characters. *) +(**The atomic document [blank n] consists of [n] blank characters. A blank + character is like an ordinary ASCII space character [char ' '], except + that blank characters that appear at the end of a line are automatically + suppressed. *) val blank: int -> document -(** [break n] is a document which consists of either [n] blank characters, - when forced to display on a single line, or a single newline character, - otherwise. Note that there is no choice at this point: choices are encoded - by the [group] combinator. *) +(**[space] is a synonym for [blank 1]. It consists of one blank character. + It is therefore not equivalent to [char ' ']. *) +val space: document + +(**The document [break n] is a breakable blank of width [n]. It produces [n] + blank characters if the printing engine is in flat mode, and a single + newline character if the printing engine is in normal mode. [break 1] is + equivalent to [ifflat (blank 1) hardline]. *) val break: int -> document -(** [doc1 ^^ doc2] is the concatenation of the documents [doc1] and [doc2]. *) -val (^^): document -> document -> document +(** {2 Composite Documents} *) -(** [nest j doc] is the document [doc], in which the indentation level has - been increased by [j], that is, in which [j] blanks have been inserted - after every newline character. Read this again: indentation is inserted - after every newline character. No indentation is inserted at the beginning - of the document. *) -val nest: int -> document -> document +(**[doc1 ^^ doc2] is the concatenation of the documents [doc1] and [doc2]. *) +val (^^): document -> document -> document -(** [group doc] encodes a choice. If possible, then the entire document [group - doc] is rendered on a single line. Otherwise, the group is dissolved, and - [doc] is rendered. There might be further groups within [doc], whose - presence will lead to further choices being explored. *) +(**[group doc] encodes a choice. If the document [doc] fits on the current + line, then it is rendered on a single line, in flat mode. (All [group] + combinators inside it are then ignored.) Otherwise, this group is + dissolved, and [doc] is rendered in normal mode. There might be more + groups within [doc], whose presence leads to further choices being + explored. *) val group: document -> document -(** [ifflat doc1 doc2] is rendered as [doc1] if part of a group that can be - successfully flattened, and is rendered as [doc2] otherwise. Use this - operation with caution. Because the pretty-printer is free to choose - between [doc1] and [doc2], these documents should be semantically - equivalent. *) +(**[ifflat doc1 doc2] is rendered as [doc1] if the printing engine is in + flat mode, that is, if the printing engine has determined that some + enclosing group fits on the current line. Otherwise, it is rendered as + [doc2]. Use this combinator with caution! Because the printing engine is + free to choose between [doc1] and [doc2], these documents must be + semantically equivalent. It is up to the user to enforce this property. *) val ifflat: document -> document -> document -(** [align doc] is the document [doc], in which the indentation level has been - set to the current column. Thus, [doc] is rendered within a box whose - upper left corner is the current position. *) +(**To render the document [nest j doc], the printing engine temporarily + increases the current indentation level by [j], then renders [doc]. The + effect of the current indentation level is as follows: every time a + newline character is emitted, it is immediately followed by [n] blank + characters, where [n] is the current indentation level. Thus, one may + think of [nest j doc] roughly as the document [doc] in which [j] blank + characters have been inserted after every newline character. *) +val nest: int -> document -> document + +(**To render [align doc], the printing engine sets the current indentation + level to the current column, then renders [doc]. In other words, the + document [doc] is rendered within a box whose upper left corner is the + current position of the printing engine. *) val align: document -> document -(** {1 Rendering documents} *) +(**A point is a pair of a line number and a column number. *) +type point = + int * int + +(**A range is a pair of points. *) +type range = + point * point + +(**The document [range hook doc] is printed like the document [doc], but + allows the caller to register a hook that is applied, when the document + is printed, to the range occupied by this document in the output text. + This offers a way of mapping positions in the output text back to + (sub)documents. *) +val range: (range -> unit) -> document -> document + +(** {1:rendering Rendering Documents} *) + +(**Three renderers are available. They offer the same API, described + by the signature {!RENDERER}, and differ only in the nature of the + output channel that they use. *) + +(**This signature describes the document renderers in a manner that + is independent of the type of the output channel. *) +module type RENDERER = sig + + (**The type of the output channel. *) + type channel -(** This renderer sends its output into an output channel. *) -module ToChannel : PPrintRenderer.RENDERER + (**The type of documents. *) + type document + + (** [pretty rfrac width channel document] pretty-prints the document + [document] into the output channel [channel]. The parameter [width] is + the maximum number of characters per line. The parameter [rfrac] is the + ribbon width, a fraction relative to [width]. The ribbon width is the + maximum number of non-indentation characters per line. *) + val pretty: float -> int -> channel -> document -> unit + + (** [compact channel document] prints the document [document] to the output + channel [channel]. No indentation is used. All newline instructions are + respected, that is, no groups are flattened. *) + val compact: channel -> document -> unit + +end + +(**This renderer sends its output into an output channel. *) +module ToChannel : RENDERER with type channel = out_channel and type document = document -(** This renderer sends its output into a memory buffer. *) -module ToBuffer : PPrintRenderer.RENDERER +(**This renderer sends its output into a memory buffer. *) +module ToBuffer : RENDERER with type channel = Buffer.t and type document = document -(** This renderer sends its output into a formatter channel. *) -module ToFormatter : PPrintRenderer.RENDERER +(**This renderer sends its output into a formatter channel. *) +module ToFormatter : RENDERER with type channel = Format.formatter and type document = document -(** {1 Defining custom documents} *) +(** {1:defining Defining Custom Documents} *) -(** A width requirement is expressed as an integer, where the value [max_int] - is reserved and represents infinity. *) +(**It is possible to define custom document constructors, provided they meet + the expectations of the printing engine. In short, the custom document + combinator {!val-custom} expects an object of class {!class-type-custom}. + This object must provide three methods. The method [requirement] must + compute the ideal width of the custom document. The methods [pretty] and + [compact] must render the custom document. For this purpose, they have + access to the {{!output}output channel} and to the {{!state}state} of the + printing engine. *) +(** A width requirement is expressed as an integer. The value [max_int] + is reserved and represents infinity. *) type requirement = int -val infinity : requirement -(** An output channel is represented abstractly as an object equipped with - methods for displaying one character and for displaying a substring. *) +(**[infinity] represents an infinite width requirement. *) +val infinity : requirement +(**An output channel is abstractly represented as an object equipped with + methods for displaying one character and for displaying a substring. *) class type output = object - (** [char c] sends the character [c] to the output channel. *) + (**[char c] sends the character [c] to the output channel. *) method char: char -> unit - (** [substring s ofs len] sends the substring of [s] delimited by the - offset [ofs] and the length [len] to the output channel. *) + (**[substring s ofs len] sends the substring of [s] delimited by the + offset [ofs] and the length [len] to the output channel. *) method substring: string -> int (* offset *) -> int (* length *) -> unit end -(** The rendering engine maintains the following internal state. Its structure - is subject to change in future versions of the library. Nevertheless, it is - exposed to the user who wishes to define custom documents. *) - +(**The internal state of the rendering engine is exposed to the user who + wishes to define custom documents. However, its structure is subject to + change in future versions of the library. *) type state = { width: int; @@ -157,10 +233,14 @@ type state = { mutable last_indent: int; (** The number of blanks that were printed at the beginning of the current - line. This field is updated (only) by the function [emit_hardline]. It - is used (only) to determine whether the ribbon width constraint is + line. This field is updated (only) when a hardline is emitted. It is + used (only) to determine whether the ribbon width constraint is respected. *) + mutable line: int; + (** The current line. This field is updated (only) when a hardline is + emitted. It is not used by the pretty-printing engine itself. *) + mutable column: int; (** The current column. This field must be updated whenever something is sent to the output channel. It is used (only) to determine whether the @@ -168,59 +248,60 @@ type state = { } -(** A custom document is defined by implementing the following methods. *) - +(**A custom document is defined by implementing an object of class + {!class-type-custom}. *) class type custom = object - (** A custom document must publish the width (i.e., the number of columns) - that it would like to occupy if it is printed on a single line (that is, - in flattening mode). The special value [infinity] means that this - document cannot be printed on a single line; this value causes any - groups that contain this document to be dissolved. This method should - in principle work in constant time. *) + (**A custom document must publish the width (i.e., the number of columns) + that it would like to occupy if printed on a single line (in flat + mode). The special value [infinity] means that this document cannot be + printed on a single line; this value causes any groups that contain + this document to be dissolved. This method should in principle work in + constant time. *) method requirement: requirement - (** The method [pretty] is used by the main rendering algorithm. It has - access to the output channel and to the algorithm's internal state, as - described above. In addition, it receives the current indentation level - and the current flattening mode (on or off). If flattening mode is on, - then the document must be printed on a single line, in a manner that is - consistent with the requirement that was published ahead of time. If - flattening mode is off, then there is no such obligation. The state must - be updated in a manner that is consistent with what is sent to the - output channel. *) + (**The method [pretty] is used by the main rendering algorithm. It has + access to the output channel and to the printing engine's internal + state. In addition, it receives the current indentation level and a + Boolean flag that tells whether the engine is currently in flat mode. + If the engine is in flat mode, then the document must be printed on a + single line, in a manner that is consistent with the width requirement + that was published ahead of time. If the engine is in normal mode, then + there is no such obligation. The state must be updated in a manner that + is consistent with what is sent to the output channel. *) method pretty: output -> state -> int -> bool -> unit - (** The method [compact] is used by the compact rendering algorithm. It has - access to the output channel only. *) + (**The method [compact] is used by the compact rendering algorithm. It + has access to the output channel only. *) method compact: output -> unit end -(** The function [custom] constructs a custom document. In other words, it - converts an object of type [custom] to a document. *) +(**[custom] constructs a custom document out an object of type + {!class-type-custom}. *) val custom: custom -> document -(** The key functions of the library are exposed, in the hope that they may be - useful to authors of custom (leaf and non-leaf) documents. In the case of - a leaf document, they can help perform certain basic functions; for - instance, applying the function [pretty] to the document [hardline] is a - simple way of printing a hardline, while respecting the indentation - parameters and updating the state in a correct manner. Similarly, applying - [pretty] to the document [blank n] is a simple way of printing [n] spaces. - In the case of a non-leaf document (i.e., one which contains - sub-documents), these functions are essential: they allow computing the - width requirement of a sub-document and displaying a sub-document. *) - -(** [requirement doc] computes the width requirement of the document [doc]. - It works in constant time. *) +(**Some of the key functions of the library are exposed, in the hope that + they may be useful to authors of custom (leaf and composite) documents. + In the case of a leaf document, they can help perform certain basic + functions; for instance, applying the function {!pretty} to the document + {!hardline} is a simple way of printing a hardline, while respecting the + indentation parameters and updating the state in a correct manner. + Similarly, applying {!pretty} to the document [blank n] is a simple way + of printing [n] blank characters. In the case of a composite document + (one that contains subdocuments), these functions are essential: they + allow computing the width requirement of a subdocument and displaying a + subdocument. *) + +(**[requirement doc] computes the width requirement of the document [doc]. + It runs in constant time. *) val requirement: document -> requirement -(** [pretty output state indent flatten doc] prints the document [doc]. See - the documentation of the method [pretty]. *) +(**[pretty output state indent flatten doc] prints the document [doc]. See + the documentation of the method [pretty] in the class + {!class-type-custom}. *) val pretty: output -> state -> int -> bool -> document -> unit -(** [compact output doc] prints the document [doc]. See the documentation of - the method [compact]. *) +(**[compact output doc] prints the document [doc]. See the documentation of + the method [compact] in the class {!class-type-custom}. *) val compact: output -> document -> unit - diff --git a/tests/menhir_tests/test10menhir/PPrintRenderer.ml b/tests/menhir_tests/test10menhir/PPrintRenderer.ml deleted file mode 100644 index 3449d6c..0000000 --- a/tests/menhir_tests/test10menhir/PPrintRenderer.ml +++ /dev/null @@ -1,37 +0,0 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) - -(** A common signature for the multiple document renderers proposed by {!PPrintEngine}. *) - -module type RENDERER = sig - - (** Output channels. *) - type channel - - (** Documents. *) - type document - - (** [pretty rfrac width channel document] pretty-prints the document - [document] into the output channel [channel]. The parameter [width] is - the maximum number of characters per line. The parameter [rfrac] is the - ribbon width, a fraction relative to [width]. The ribbon width is the - maximum number of non-indentation characters per line. *) - val pretty: float -> int -> channel -> document -> unit - - (** [compact channel document] prints the document [document] to the output - channel [channel]. No indentation is used. All newline instructions are - respected, that is, no groups are flattened. *) - val compact: channel -> document -> unit - -end - diff --git a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintCombinators.ml b/tests/menhir_tests/test10menhir_with_aux_args/PPrint.ml similarity index 61% rename from tests/menhir_tests/test10menhir_with_aux_rules/PPrintCombinators.ml rename to tests/menhir_tests/test10menhir_with_aux_args/PPrint.ml index 7049987..da55896 100644 --- a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintCombinators.ml +++ b/tests/menhir_tests/test10menhir_with_aux_args/PPrint.ml @@ -1,19 +1,19 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) - -open PPrintEngine - -(* ------------------------------------------------------------------------- *) +(******************************************************************************) +(* *) +(* PPrint *) +(* *) +(* François Pottier, Inria Paris *) +(* Nicolas Pouillard *) +(* *) +(* Copyright 2007-2022 Inria. All rights reserved. This file is *) +(* distributed under the terms of the GNU Library General Public *) +(* License, with an exception, as described in the file LICENSE. *) +(* *) +(******************************************************************************) + +include PPrintEngine + +(* -------------------------------------------------------------------------- *) (* Predefined single-character documents. *) @@ -31,7 +31,6 @@ let bquote = char '`' let semi = char ';' let colon = char ':' let comma = char ',' -let space = char ' ' let dot = char '.' let sharp = char '#' let slash = char '/' @@ -51,7 +50,7 @@ let underscore = char '_' let bang = char '!' let bar = char '|' -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Repetition. *) @@ -67,7 +66,7 @@ let repeat n doc = in loop n doc empty -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Delimiters. *) @@ -83,7 +82,7 @@ let parens = enclose lparen rparen let angles = enclose langle rangle let brackets = enclose lbracket rbracket -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Some functions on lists. *) @@ -97,7 +96,7 @@ let foldli (f : int -> 'b -> 'a -> 'b) (accu : 'b) (xs : 'a list) : 'b = f i accu x ) accu xs -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Working with lists of documents. *) @@ -147,7 +146,7 @@ let optional f = function | Some x -> f x -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Text. *) @@ -210,7 +209,7 @@ let words s = let n = String.length s in (* A two-state finite automaton. *) (* In this state, we have skipped at least one blank character. *) - let rec skipping accu i = + let rec skipping accu i = if i = n then (* There was whitespace at the end. Drop it. *) accu @@ -235,7 +234,7 @@ let words s = | '\n' | '\r' -> (* A new word has been identified. *) - let accu = substring s i (j - i) :: accu in + let accu = substring s i (j - i) :: accu in skipping accu (j + 1) | _ -> (* Continue inside the current word. *) @@ -252,7 +251,7 @@ let flow_map sep f docs = (* This idiom allows beginning a new line if [doc] does not fit on the current line. *) group (sep ^^ f doc) - ) empty docs + ) empty docs let flow sep docs = flow_map sep (fun x -> x) docs @@ -260,8 +259,7 @@ let flow sep docs = let url s = flow (break 0) (split (function '/' | '.' -> true | _ -> false) s) -(* ------------------------------------------------------------------------- *) - +(* -------------------------------------------------------------------------- *) (* Alignment and indentation. *) let hang i d = @@ -281,11 +279,6 @@ let (^//^) = let jump n b y = group (nest n (break b ^^ y)) -(* Deprecated. -let ( ^@^ ) x y = group (x ^/^ y) -let ( ^@@^ ) x y = group (nest 2 (x ^/^ y)) -*) - let infix n b op x y = prefix n b (x ^^ blank b ^^ op) y @@ -309,3 +302,167 @@ let surround_separate_map n b void opening sep closing f xs = | _ :: _ -> surround n b opening (separate_map sep f xs) closing +(* -------------------------------------------------------------------------- *) +(* Printing OCaml values. *) + +module OCaml = struct + +open Printf + +type constructor = string +type type_name = string +type record_field = string +type tag = int + +(* -------------------------------------------------------------------------- *) + +(* This internal [sprintf]-like function produces a document. We use [string], + as opposed to [arbitrary_string], because the strings that we produce will + never contain a newline character. *) + +let dsprintf format = + ksprintf string format + +(* -------------------------------------------------------------------------- *) + +(* Nicolas prefers using this code as opposed to just [sprintf "%g"] or + [sprintf "%f"]. The latter print [inf] and [-inf], whereas OCaml + understands [infinity] and [neg_infinity]. [sprintf "%g"] does not add a + trailing dot when the number happens to be an integral number. [sprintf + "%F"] seems to lose precision and ignores the precision modifier. *) + +let valid_float_lexeme (s : string) : string = + let l = String.length s in + let rec loop i = + if i >= l then + (* If we reach the end of the string and have found only characters in + the set '0' .. '9' and '-', then this string will be considered as an + integer literal by OCaml. Adding a trailing dot makes it a float + literal. *) + s ^ "." + else + match s.[i] with + | '0' .. '9' | '-' -> loop (i + 1) + | _ -> s + in loop 0 + +(* This function constructs a string representation of a floating point + number. This representation is supposed to be accepted by OCaml as a + valid floating point literal. *) + +let float_representation (f : float) : string = + match classify_float f with + | FP_nan -> + "nan" + | FP_infinite -> + if f < 0.0 then "neg_infinity" else "infinity" + | _ -> + (* Try increasing precisions and validate. *) + let s = sprintf "%.12g" f in + if f = float_of_string s then valid_float_lexeme s else + let s = sprintf "%.15g" f in + if f = float_of_string s then valid_float_lexeme s else + sprintf "%.18g" f + +(* -------------------------------------------------------------------------- *) + +(* A few constants and combinators, used below. *) + +let some = + string "Some" + +let none = + string "None" + +let lbracketbar = + string "[|" + +let rbracketbar = + string "|]" + +let seq1 opening separator closing = + surround_separate 2 0 + (opening ^^ closing) opening (separator ^^ break 1) closing + +let seq2 opening separator closing = + surround_separate_map 2 1 + (opening ^^ closing) opening (separator ^^ break 1) closing + +(* -------------------------------------------------------------------------- *) + +(* The following functions are printers for many types of OCaml values. *) + +(* There is no protection against cyclic values. *) + +let tuple = + seq1 lparen comma rparen + +let variant _ cons _ args = + match args with + | [] -> + !^cons + | _ :: _ -> + !^cons ^^ tuple args + +let record _ fields = + seq2 lbrace semi rbrace (fun (k, v) -> infix 2 1 equals !^k v) fields + +let option f = function + | None -> + none + | Some x -> + some ^^ tuple [f x] + +let list f xs = + seq2 lbracket semi rbracket f xs + +let flowing_list f xs = + group (lbracket ^^ space ^^ nest 2 ( + flow_map (semi ^^ break 1) f xs + ) ^^ space ^^ rbracket) + +let array f xs = + seq2 lbracketbar semi rbracketbar f (Array.to_list xs) + +let flowing_array f xs = + group (lbracketbar ^^ space ^^ nest 2 ( + flow_map (semi ^^ break 1) f (Array.to_list xs) + ) ^^ space ^^ rbracketbar) + +let ref f x = + record "ref" ["contents", f !x] + +let float f = + string (float_representation f) + +let int = + dsprintf "%d" + +let int32 = + dsprintf "%ld" + +let int64 = + dsprintf "%Ld" + +let nativeint = + dsprintf "%nd" + +let char = + dsprintf "%C" + +let bool = + dsprintf "%B" + +let unit = + dsprintf "()" + +let string = + dsprintf "%S" + +let unknown tyname _ = + dsprintf "" tyname + +type representation = + document + +end (* OCaml *) diff --git a/tests/menhir_tests/test10menhir_with_aux_args/PPrint.mli b/tests/menhir_tests/test10menhir_with_aux_args/PPrint.mli new file mode 100644 index 0000000..89eeeeb --- /dev/null +++ b/tests/menhir_tests/test10menhir_with_aux_args/PPrint.mli @@ -0,0 +1,345 @@ +(******************************************************************************) +(* *) +(* PPrint *) +(* *) +(* François Pottier, Inria Paris *) +(* Nicolas Pouillard *) +(* *) +(* Copyright 2007-2022 Inria. All rights reserved. This file is *) +(* distributed under the terms of the GNU Library General Public *) +(* License, with an exception, as described in the file LICENSE. *) +(* *) +(******************************************************************************) + +include module type of PPrintEngine (** @inline *) + +(** {1:combinators High-Level Combinators} *) + +(** {2 Single Characters} *) + +(**The following atomic documents consist of a single character. Each of + them is a synonym for the application of {!char} to some constant + character. For instance, {!lparen} is a synonym for [char '(']. *) + +val lparen: document +val rparen: document +val langle: document +val rangle: document +val lbrace: document +val rbrace: document +val lbracket: document +val rbracket: document +val squote: document +val dquote: document +val bquote: document +val semi: document +val colon: document +val comma: document +val dot: document +val sharp: document +val slash: document +val backslash: document +val equals: document +val qmark: document +val tilde: document +val at: document +val percent: document +val dollar: document +val caret: document +val ampersand: document +val star: document +val plus: document +val minus: document +val underscore: document +val bang: document +val bar: document + +(** {2 Delimiters} *) + +(**[precede l x] is [l ^^ x]. *) +val precede: document -> document -> document + +(**[terminate r x] is [x ^^ r]. *) +val terminate: document -> document -> document + +(**[enclose l r x] is [l ^^ x ^^ r]. *) +val enclose: document -> document -> document -> document + +(**The following combinators enclose a document within a pair of delimiters. + They are partial applications of [enclose]. No whitespace or line break + is introduced. *) + +val squotes: document -> document +val dquotes: document -> document +val bquotes: document -> document +val braces: document -> document +val parens: document -> document +val angles: document -> document +val brackets: document -> document + +(** {2 Repetition} *) + +(**[twice doc] is the document obtained by concatenating two copies of + the document [doc]. *) +val twice: document -> document + +(**[repeat n doc] is the document obtained by concatenating [n] copies of + the document [doc]. *) +val repeat: int -> document -> document + +(** {2 Lists and Options} *) + +(**[concat docs] is the concatenation of the documents in the list [docs]. *) +val concat: document list -> document + +(**[separate sep docs] is the concatenation of the documents in the list + [docs]. The separator [sep] is inserted between every two adjacent + documents. *) +val separate: document -> document list -> document + +(**[concat_map f xs] is equivalent to [concat (List.map f xs)]. *) +val concat_map: ('a -> document) -> 'a list -> document + +(**[separate_map sep f xs] is equivalent to [separate sep (List.map f xs)]. *) +val separate_map: document -> ('a -> document) -> 'a list -> document + +(**[separate2 sep last_sep docs] is the concatenation of the documents in + the list [docs]. The separator [sep] is inserted between every two + adjacent documents, except between the last two documents, where the + separator [last_sep] is used instead. *) +val separate2: document -> document -> document list -> document + +(**[optional f None] is the empty document. [optional f (Some x)] is + the document [f x]. *) +val optional: ('a -> document) -> 'a option -> document + +(** {2 Text} *) + +(**[lines s] is the list of documents obtained by splitting [s] at newline + characters, and turning each line into a document via [substring]. This + code is not UTF-8 aware. *) +val lines: string -> document list + +(**[arbitrary_string s] is equivalent to [separate (break 1) (lines s)]. + It is analogous to [string s], but is valid even if the string [s] + contains newline characters. *) +val arbitrary_string: string -> document + +(**[words s] is the list of documents obtained by splitting [s] at whitespace + characters, and turning each word into a document via [substring]. All + whitespace is discarded. This code is not UTF-8 aware. *) +val words: string -> document list + +(**[split ok s] splits the string [s] before and after every occurrence of a + character that satisfies the predicate [ok]. The substrings thus obtained + are turned into documents, and a list of documents is returned. No + information is lost: the concatenation of the documents yields the + original string. This code is not UTF-8 aware. *) +val split: (char -> bool) -> string -> document list + +(**[flow sep docs] separates the documents in the list [docs] with the + separator [sep] and arranges for a new line to begin whenever a document + does not fit on the current line. This is useful for typesetting + free-flowing, ragged-right text. A typical choice of [sep] is [break b], + where [b] is the number of spaces that must be inserted between two + consecutive words (when displayed on the same line). *) +val flow: document -> document list -> document + +(**[flow_map sep f docs] is equivalent to [flow sep (List.map f docs)]. *) +val flow_map: document -> ('a -> document) -> 'a list -> document + +(**[url s] is a possible way of displaying the URL [s]. A potential line + break is inserted immediately before and immediately after every slash + and dot character. *) +val url: string -> document + +(** {2 Alignment and Indentation} *) + +(**[hang n doc] is analogous to [align], but additionally indents all lines, + except the first one, by [n]. Thus, the text in the box forms a hanging + indent. *) +val hang: int -> document -> document + +(**[prefix n b left right] has the following flat layout: + {[ + left right + ]} + and the following non-flat layout: + {[ + left + right + ]} + The parameter [n] controls the nesting of [right] (when not flat). + The parameter [b] controls the number of spaces between [left] and [right] + (when flat). *) +val prefix: int -> int -> document -> document -> document + +(**[jump n b right] is equivalent to [prefix n b empty right]. *) +val jump: int -> int -> document -> document + +(**[infix n b middle left right] has the following flat layout: + {[ + left middle right + ]} + and the following non-flat layout: + {[ + left middle + right + ]} + The parameter [n] controls the nesting of [right] (when not flat). + The parameter [b] controls the number of spaces between [left] and [middle] + (always) and between [middle] and [right] (when flat). *) +val infix: int -> int -> document -> document -> document -> document + +(**[surround n b opening contents closing] has the following flat layout: + {[ + opening contents closing + ]} + and the following non-flat layout: + {[ + opening + contents + closing + ]} + The parameter [n] controls the nesting of [contents] (when not flat). + The parameter [b] controls the number of spaces between [opening] and + [contents] and between [contents] and [closing] (when flat). +*) +val surround: int -> int -> document -> document -> document -> document + +(**[soft_surround] is analogous to [surround], but involves more than one + group, so it offers possibilities other than the completely flat layout + (where [opening], [contents], and [closing] appear on a single line) and + the completely developed layout (where [opening], [contents], and + [closing] appear on separate lines). It tries to place the beginning of + [contents] on the same line as [opening], and to place [closing] on the + same line as the end of [contents], if possible. *) +val soft_surround: int -> int -> document -> document -> document -> document + +(**[surround_separate n b void opening sep closing docs] is equivalent to + [surround n b opening (separate sep docs) closing], except when the list + [docs] is empty, in which case it reduces to [void]. *) +val surround_separate: + int -> int -> + document -> document -> document -> document -> + document list -> document + +(**[surround_separate_map n b void opening sep closing f xs] is equivalent + to [surround_separate n b void opening sep closing (List.map f xs)]. *) +val surround_separate_map: + int -> int -> + document -> document -> document -> document -> + ('a -> document) -> 'a list -> document + +(** {2 Short-Hands} *) + +(**[!^s] is a short-hand for [string s]. *) +val ( !^ ) : string -> document + +(**[x ^/^ y] separates [x] and [y] with a breakable space. + It is a short-hand for [x ^^ break 1 ^^ y]. *) +val ( ^/^ ) : document -> document -> document + +(**[x ^//^ y] is a short-hand for [prefix 2 1 x y]. *) +val ( ^//^ ) : document -> document -> document + +(** {1:ocaml Printing OCaml Values} *) + +(**This module offers document combinators that help print OCaml values. The + strings produced by rendering these documents are supposed to be accepted + by the OCaml parser as valid values. + + These functions do {i not} distinguish between mutable and immutable + values. They do {i not} recognize sharing, and do {i not} incorporate a + protection against cyclic values. *) +module OCaml : sig + +(* The signature of this module is compatible with that expected by the + [camlp4] generator [Camlp4RepresentationGenerator]. This explains why + some functions have unused parameters. This is also the reason why + there is a type [representation]. *) + +type constructor = string +type type_name = string +type record_field = string +type tag = int + +(**[variant _ dc _ args] represents a constructed value whose data + constructor is [dc] and whose arguments are [args]. The other two + parameters are presently unused. *) +val variant : type_name -> constructor -> tag -> document list -> document + +(**[record _ fields] represents a record value whose fields are [fields]. + The other parameter is presently unused. *) +val record : type_name -> (record_field * document) list -> document + +(**[tuple args] represents a tuple value whose components are [args]. *) +val tuple : document list -> document + +(**[string s] represents the literal string [s]. *) +val string : string -> document + +(**[int i] represents the literal integer [i]. *) +val int : int -> document + +(**[int32 i] represents the literal 32-bit integer [i]. *) +val int32 : int32 -> document + +(**[int64 i] represents the literal 64-bit integer [i]. *) +val int64 : int64 -> document + +(**[nativeint i] represents the literal native integer [i]. *) +val nativeint : nativeint -> document + +(**[float f] represents the literal floating-point number [f]. *) +val float : float -> document + +(**[char c] represents the literal character [c]. *) +val char : char -> document + +(**[bool b] represents the Boolean value [b]. *) +val bool : bool -> document + +(**[unit] represents the unit constant [()]. *) +val unit : document + +(**[option f o] represents the option [o]. The representation of the + element, if present, is computed by the function [f]. *) +val option : ('a -> document) -> 'a option -> document + +(**[list f xs] represents the list [xs]. The representation of each element + is computed by the function [f]. If the whole list fits on a single line, + then it is printed on a single line; otherwise each element is printed on + a separate line. *) +val list : ('a -> document) -> 'a list -> document + +(**[flowing_list f xs] represents the list [xs]. The representation of each + element is computed by the function [f]. As many elements are possible + are printed on each line. *) +val flowing_list : ('a -> document) -> 'a list -> document + +(**[array f xs] represents the array [xs]. The representation of each + element is computed by the function [f]. If the whole array fits on a + single line, then it is printed on a single line; otherwise each element + is printed on a separate line. *) +val array : ('a -> document) -> 'a array -> document + +(**[flowing_array f xs] represents the array [xs]. The representation of + each element is computed by the function [f]. As many elements are + possible are printed on each line. *) +val flowing_array : ('a -> document) -> 'a array -> document + +(**[ref r] represents the reference [r]. The representation of the content + is computed by the function [f]. *) +val ref : ('a -> document) -> 'a ref -> document + +(** [unknown t _] represents an unknown value of type [t]. It is rendered + as a string of the form []. *) +val unknown : type_name -> 'a -> document + +(**/**) + +type representation = + document + +end (* OCaml *) diff --git a/tests/menhir_tests/test10menhir_with_aux_args/PPrintCombinators.mli b/tests/menhir_tests/test10menhir_with_aux_args/PPrintCombinators.mli deleted file mode 100644 index c538cb3..0000000 --- a/tests/menhir_tests/test10menhir_with_aux_args/PPrintCombinators.mli +++ /dev/null @@ -1,236 +0,0 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) - -open PPrintEngine - -(** A set of high-level combinators for building documents. *) - -(** {1 Single characters} *) - -(** The following constant documents consist of a single character. *) - -val lparen: document -val rparen: document -val langle: document -val rangle: document -val lbrace: document -val rbrace: document -val lbracket: document -val rbracket: document -val squote: document -val dquote: document -val bquote: document -val semi: document -val colon: document -val comma: document -val space: document -val dot: document -val sharp: document -val slash: document -val backslash: document -val equals: document -val qmark: document -val tilde: document -val at: document -val percent: document -val dollar: document -val caret: document -val ampersand: document -val star: document -val plus: document -val minus: document -val underscore: document -val bang: document -val bar: document - -(** {1 Delimiters} *) - -(** [precede l x] is [l ^^ x]. *) -val precede: document -> document -> document - -(** [terminate r x] is [x ^^ r]. *) -val terminate: document -> document -> document - -(** [enclose l r x] is [l ^^ x ^^ r]. *) -val enclose: document -> document -> document -> document - -(** The following combinators enclose a document within a pair of delimiters. - They are partial applications of [enclose]. No whitespace or line break is - introduced. *) - -val squotes: document -> document -val dquotes: document -> document -val bquotes: document -> document -val braces: document -> document -val parens: document -> document -val angles: document -> document -val brackets: document -> document - -(** {1 Repetition} *) - -(** [twice doc] is the document obtained by concatenating two copies of - the document [doc]. *) -val twice: document -> document - -(** [repeat n doc] is the document obtained by concatenating [n] copies of - the document [doc]. *) -val repeat: int -> document -> document - -(** {1 Lists and options} *) - -(** [concat docs] is the concatenation of the documents in the list [docs]. *) -val concat: document list -> document - -(** [separate sep docs] is the concatenation of the documents in the list - [docs]. The separator [sep] is inserted between every two adjacent - documents. *) -val separate: document -> document list -> document - -(** [concat_map f xs] is equivalent to [concat (List.map f xs)]. *) -val concat_map: ('a -> document) -> 'a list -> document - -(** [separate_map sep f xs] is equivalent to [separate sep (List.map f xs)]. *) -val separate_map: document -> ('a -> document) -> 'a list -> document - -(** [separate2 sep last_sep docs] is the concatenation of the documents in the - list [docs]. The separator [sep] is inserted between every two adjacent - documents, except between the last two documents, where the separator - [last_sep] is used instead. *) -val separate2: document -> document -> document list -> document - -(** [optional f None] is the empty document. [optional f (Some x)] is - the document [f x]. *) -val optional: ('a -> document) -> 'a option -> document - -(** {1 Text} *) - -(** [lines s] is the list of documents obtained by splitting [s] at newline - characters, and turning each line into a document via [substring]. This - code is not UTF-8 aware. *) -val lines: string -> document list - -(** [arbitrary_string s] is equivalent to [separate (break 1) (lines s)]. - It is analogous to [string s], but is valid even if the string [s] - contains newline characters. *) -val arbitrary_string: string -> document - -(** [words s] is the list of documents obtained by splitting [s] at whitespace - characters, and turning each word into a document via [substring]. All - whitespace is discarded. This code is not UTF-8 aware. *) -val words: string -> document list - -(** [split ok s] splits the string [s] before and after every occurrence of a - character that satisfies the predicate [ok]. The substrings thus obtained - are turned into documents, and a list of documents is returned. No - information is lost: the concatenation of the documents yields the - original string. This code is not UTF-8 aware. *) -val split: (char -> bool) -> string -> document list - -(** [flow sep docs] separates the documents in the list [docs] with the - separator [sep] and arranges for a new line to begin whenever a document - does not fit on the current line. This is useful for typesetting - free-flowing, ragged-right text. A typical choice of [sep] is [break b], - where [b] is the number of spaces that must be inserted between two - consecutive words (when displayed on the same line). *) -val flow: document -> document list -> document - -(** [flow_map sep f docs] is equivalent to [flow sep (List.map f docs)]. *) -val flow_map: document -> ('a -> document) -> 'a list -> document - -(** [url s] is a possible way of displaying the URL [s]. A potential line - break is inserted immediately before and immediately after every slash - and dot character. *) -val url: string -> document - -(** {1 Alignment and indentation} *) - -(* [hang n doc] is analogous to [align], but additionally indents - all lines, except the first one, by [n]. Thus, the text in the - box forms a hanging indent. *) -val hang: int -> document -> document - -(** [prefix n b left right] has the following flat layout: {[ -left right -]} -and the following non-flat layout: -{[ -left - right -]} -The parameter [n] controls the nesting of [right] (when not flat). -The parameter [b] controls the number of spaces between [left] and [right] -(when flat). - *) -val prefix: int -> int -> document -> document -> document - -(** [jump n b right] is equivalent to [prefix n b empty right]. *) -val jump: int -> int -> document -> document - -(** [infix n b middle left right] has the following flat layout: {[ -left middle right -]} -and the following non-flat layout: {[ -left middle - right -]} -The parameter [n] controls the nesting of [right] (when not flat). -The parameter [b] controls the number of spaces between [left] and [middle] -(always) and between [middle] and [right] (when flat). -*) -val infix: int -> int -> document -> document -> document -> document - -(** [surround n b opening contents closing] has the following flat layout: {[ -opening contents closing -]} -and the following non-flat layout: {[ -opening - contents -closing -]} -The parameter [n] controls the nesting of [contents] (when not flat). -The parameter [b] controls the number of spaces between [opening] and [contents] -and between [contents] and [closing] (when flat). -*) -val surround: int -> int -> document -> document -> document -> document - -(** [soft_surround] is analogous to [surround], but involves more than one - group, so it offers possibilities other than the completely flat layout - (where [opening], [contents], and [closing] appear on a single line) and - the completely developed layout (where [opening], [contents], and - [closing] appear on separate lines). It tries to place the beginning of - [contents] on the same line as [opening], and to place [closing] on the - same line as the end of [contents], if possible. -*) -val soft_surround: int -> int -> document -> document -> document -> document - -(** [surround_separate n b void opening sep closing docs] is equivalent to - [surround n b opening (separate sep docs) closing], except when the - list [docs] is empty, in which case it reduces to [void]. *) -val surround_separate: int -> int -> document -> document -> document -> document -> document list -> document - -(** [surround_separate_map n b void opening sep closing f xs] is equivalent to - [surround_separate n b void opening sep closing (List.map f xs)]. *) -val surround_separate_map: int -> int -> document -> document -> document -> document -> ('a -> document) -> 'a list -> document - -(** {1 Short-hands} *) - -(** [!^s] is a short-hand for [string s]. *) -val ( !^ ) : string -> document - -(** [x ^/^ y] separates [x] and [y] with a breakable space. - It is a short-hand for [x ^^ break 1 ^^ y]. *) -val ( ^/^ ) : document -> document -> document - -(** [x ^//^ y] is a short-hand for [prefix 2 1 x y]. *) -val ( ^//^ ) : document -> document -> document - diff --git a/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.ml b/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.ml index 2a78363..3131893 100644 --- a/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.ml +++ b/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.ml @@ -1,15 +1,23 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) +(******************************************************************************) +(* *) +(* PPrint *) +(* *) +(* François Pottier, Inria Paris *) +(* Nicolas Pouillard *) +(* *) +(* Copyright 2007-2022 Inria. All rights reserved. This file is *) +(* distributed under the terms of the GNU Library General Public *) +(* License, with an exception, as described in the file LICENSE. *) +(* *) +(******************************************************************************) + +(** A point is a pair of a line number and a column number. *) +type point = + int * int + +(** A range is a pair of points. *) +type range = + point * point (* ------------------------------------------------------------------------- *) @@ -51,6 +59,82 @@ class type output = object end +(* ------------------------------------------------------------------------- *) + +(* Printing blank space. This is used both internally (to emit indentation + characters) and via the public combinator [blank]. *) + +let blank_length = + 80 + +let blank_buffer = + String.make blank_length ' ' + +let rec blanks (output : output) n = + if n <= 0 then + () + else if n <= blank_length then + output#substring blank_buffer 0 n + else begin + output#substring blank_buffer 0 blank_length; + blanks output (n - blank_length) + end + +(* ------------------------------------------------------------------------- *) + +(* The class [buffering] implements a wrapper that delays the printing of + blank characters. This includes indentation characters and characters + produced by the combinator [blank]. The printing of these characters is + delayed until it is known that they are followed by something on the same + line; if they are not followed with anything, then it is canceled. + + The actual printing task is delegated to the object [delegate], whose type + is [output]; the new object has type [output] as well. *) + +class buffering (delegate : output) : output = object (self) + + (* The number of blank characters that are withholding. *) + val mutable buffered = 0 + + (* [flush] sends out the blank characters that have been withheld. *) + method private flush = + blanks delegate buffered; + buffered <- 0 + + method char c : unit = + begin match c with + | '\n' -> + (* The current line ends here. Any blank characters that were withheld + are destroyed. This is where we avoid printing blank characters if + nothing follows them. *) + buffered <- 0 + | _ -> + (* The current line is nonempty. Any blank characters that were + withheld can now be flushed. *) + self#flush + end; + (* Print this character as usual. *) + delegate#char c + + method substring s pos len = + (* If this is a string of length zero, then there is nothing to do. *) + if len = 0 then + () + (* If this is a blank string (which we recognize by its address), then + its content is withheld. *) + else if s == blank_buffer then + buffered <- buffered + len + (* If this is not a blank string, then the blank characters that were + withheld up to this point can now be flushed. *) + else begin + self#flush; + delegate#substring s pos len + end + +end + +(* ------------------------------------------------------------------------- *) + (* Three kinds of output channels are wrapped so as to satisfy the above interface: OCaml output channels, OCaml memory buffers, and OCaml formatters. *) @@ -72,8 +156,17 @@ class buffer_output buffer = object end class formatter_output fmt = object - method char = Format.pp_print_char fmt - method substring = fst (Format.pp_get_formatter_output_functions fmt ()) + method char = function + | '\n' -> Format.pp_force_newline fmt () + | ' ' -> Format.pp_print_space fmt () + | c -> Format.pp_print_char fmt c + + method substring str ofs len = + Format.pp_print_text fmt ( + if ofs = 0 && len = String.length str + then str + else String.sub str ofs len + ) end (* ------------------------------------------------------------------------- *) @@ -94,10 +187,14 @@ type state = { mutable last_indent: int; (** The number of blanks that were printed at the beginning of the current - line. This field is updated (only) by the function [emit_hardline]. It - is used (only) to determine whether the ribbon width constraint is + line. This field is updated (only) when a hardline is emitted. It is + used (only) to determine whether the ribbon width constraint is respected. *) + mutable line: int; + (** The current line. This field is updated (only) when a hardline is + emitted. It is not used by the pretty-printing engine itself. *) + mutable column: int; (** The current column. This field must be updated whenever something is sent to the output channel. It is used (only) to determine whether the @@ -113,6 +210,7 @@ let initial rfrac width = { width = width; ribbon = max 0 (min width (truncate (float_of_int width *. rfrac))); last_indent = 0; + line = 0; column = 0 } @@ -245,6 +343,12 @@ type document = | Align of requirement * document + (* [Range (req, hook, doc)] is printed like [doc]. After it is printed, the + function [hook] is applied to the range that is occupied by [doc] in the + output. *) + + | Range of requirement * (range -> unit) * document + (* [Custom (req, f)] is a document whose appearance is user-defined. *) | Custom of custom @@ -276,7 +380,8 @@ let rec requirement = function | Cat (req, _, _) | Nest (req, _, _) | Group (req, _) - | Align (req, _) -> + | Align (req, _) + | Range (req, _, _) -> (* These nodes store their requirement -- which is computed when the node is constructed -- so as to allow us to answer in constant time here. *) @@ -298,7 +403,7 @@ let char c = Char c let space = - char ' ' + Blank 1 let string s = String s @@ -332,6 +437,9 @@ let utf8_length s = let utf8string s = fancystring s (utf8_length s) +let utf8format f = + Printf.ksprintf utf8string f + let hardline = HardLine @@ -339,8 +447,6 @@ let blank n = match n with | 0 -> empty - | 1 -> - space | _ -> Blank n @@ -394,6 +500,9 @@ let group x = let align x = Align (requirement x, x) +let range hook x = + Range (requirement x, hook, x) + let custom c = (* Sanity check. *) assert (c#requirement >= 0); @@ -401,26 +510,6 @@ let custom c = (* ------------------------------------------------------------------------- *) -(* Printing blank space (indentation characters). *) - -let blank_length = - 80 - -let blank_buffer = - String.make blank_length ' ' - -let rec blanks output n = - if n <= 0 then - () - else if n <= blank_length then - output#substring blank_buffer 0 n - else begin - output#substring blank_buffer 0 blank_length; - blanks output (n - blank_length) - end - -(* ------------------------------------------------------------------------- *) - (* This function expresses the following invariant: if we are in flattening mode, then we must be within bounds, i.e. the width and ribbon width constraints must be respected. *) @@ -445,12 +534,14 @@ let ok state flatten : bool = manner in which the document is rendered. *) (* The code is written in tail-recursive style, so as to avoid running out of - stack space if the document is very deep. Its explicit continuation can be - viewed as a sequence of pending calls to [pretty]. *) + stack space if the document is very deep. Each [KCons] cell in a + continuation represents a pending call to [pretty]. Each [KRange] cell + represents a pending call to a user-provided range hook. *) type cont = | KNil | KCons of int * bool * document * cont + | KRange of (range -> unit) * point * cont let rec pretty (output : output) @@ -498,6 +589,7 @@ let rec pretty (* Emit a hardline. *) output#char '\n'; blanks output indent; + state.line <- state.line + 1; state.column <- indent; state.last_indent <- indent; (* Continue. *) @@ -537,6 +629,10 @@ let rec pretty (* assert (state.column > state.last_indent); *) pretty output state state.column flatten doc cont + | Range (_, hook, doc) -> + let start : point = (state.line, state.column) in + pretty output state indent flatten doc (KRange (hook, start, cont)) + | Custom c -> (* Invoke the document's custom rendering function. *) c#pretty output state indent flatten; @@ -550,6 +646,10 @@ and continue output state = function () | KCons (indent, flatten, doc, cont) -> pretty output state indent flatten doc cont + | KRange (hook, start, cont) -> + let finish : point = (state.line, state.column) in + hook (start, finish); + continue output state cont (* Publish a version of [pretty] that does not take an explicit continuation. This function may be used by authors of custom documents. We do not expose @@ -575,7 +675,7 @@ let rec compact output doc cont = let len = String.length s in output#substring s 0 len; continue output cont - | FancyString (s, ofs, len, apparent_length) -> + | FancyString (s, ofs, len, _apparent_length) -> output#substring s ofs len; continue output cont | Blank n -> @@ -589,7 +689,8 @@ let rec compact output doc cont = | IfFlat (doc, _) | Nest (_, _, doc) | Group (_, doc) - | Align (_, doc) -> + | Align (_, doc) + | Range (_, _, doc) -> compact output doc cont | Custom c -> (* Invoke the document's custom rendering function. *) @@ -612,13 +713,21 @@ let compact output doc = (* This is just boilerplate. *) +module type RENDERER = sig + type channel + type document + val pretty: float -> int -> channel -> document -> unit + val compact: channel -> document -> unit +end + module MakeRenderer (X : sig type channel val output: channel -> output -end) = struct +end) +: RENDERER with type channel = X.channel and type document = document += struct type channel = X.channel - type dummy = document - type document = dummy + type nonrec document = document let pretty rfrac width channel doc = pretty (X.output channel) (initial rfrac width) 0 false doc let compact channel doc = compact (X.output channel) doc end @@ -626,17 +735,17 @@ end module ToChannel = MakeRenderer(struct type channel = out_channel - let output = new channel_output + let output channel = new buffering (new channel_output channel) end) module ToBuffer = MakeRenderer(struct type channel = Buffer.t - let output = new buffer_output + let output buffer = new buffering (new buffer_output buffer) end) module ToFormatter = MakeRenderer(struct type channel = Format.formatter - let output = new formatter_output + let output fmt = new buffering (new formatter_output fmt) end) diff --git a/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.mli b/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.mli index eda61a6..4d03b12 100644 --- a/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.mli +++ b/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.mli @@ -1,150 +1,226 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) - -(** A pretty-printing engine and a set of basic document combinators. *) - -(** {1 Building documents} *) - -(** Documents must be built in memory before they are rendered. This may seem - costly, but it is a simple approach, and works well. *) - -(** The following operations form a set of basic (low-level) combinators for - building documents. On top of these combinators, higher-level combinators - can be defined: see {!PPrintCombinators}. *) - -(** This is the abstract type of documents. *) +(******************************************************************************) +(* *) +(* PPrint *) +(* *) +(* François Pottier, Inria Paris *) +(* Nicolas Pouillard *) +(* *) +(* Copyright 2007-2022 Inria. All rights reserved. This file is *) +(* distributed under the terms of the GNU Library General Public *) +(* License, with an exception, as described in the file LICENSE. *) +(* *) +(******************************************************************************) + +(**[PPrint] is an OCaml library for {b pretty-printing textual documents}. + It takes care of {b indentation and line breaks}, and is typically used + to {b pretty-print code}. *) + +(** {1:building Building Documents} *) + +(**The abstract type of documents. *) type document -(** The following basic (low-level) combinators allow constructing documents. *) +(** {2 Atomic Documents} *) -(** [empty] is the empty document. *) +(**[empty] is the empty document. *) val empty: document -(** [char c] is a document that consists of the single character [c]. This - character must not be a newline. *) +(**[char c] is an atomic document that consists of the single character [c]. + This character must not be a newline character. *) val char: char -> document -(** [string s] is a document that consists of the string [s]. This string must - not contain a newline. *) +(**[string s] is an atomic document that consists of the string [s]. This + string must not contain a newline. The printing engine assumes that the + ideal width of this string is [String.length s]. This assumption is safe + if this is an ASCII string. Otherwise, {!fancystring} or {!utf8string} + should be preferred. *) val string: string -> document -(** [substring s ofs len] is a document that consists of the portion of the - string [s] delimited by the offset [ofs] and the length [len]. This - portion must not contain a newline. *) +(**[substring s ofs len] is an atomic document that consists of the portion + of the string [s] delimited by the offset [ofs] and the length [len]. + This portion must not contain a newline. [substring s ofs len] is + equivalent to [string (String.sub s ofs len)], but is expected to be more + efficient, as the substring is not actually extracted. *) val substring: string -> int -> int -> document -(** [fancystring s apparent_length] is a document that consists of the string - [s]. This string must not contain a newline. The string may contain fancy - characters: color escape characters, UTF-8 or multi-byte characters, - etc. Thus, its apparent length (which measures how many columns the text - will take up on screen) differs from its length in bytes. *) +(**[fancystring s alen] is an atomic document that consists of the string + [s]. This string must not contain a newline. The string may contain fancy + characters: color escape characters, UTF-8 characters, etc. Thus, its + apparent length (which measures how many columns the text will take up on + screen) differs from its length in bytes. The printing engine assumes + that its apparent length is [alen]. *) val fancystring: string -> int -> document -(** [fancysubstring s ofs len apparent_length] is a document that consists of - the portion of the string [s] delimited by the offset [ofs] and the length - [len]. This portion must contain a newline. The string may contain fancy - characters. *) +(**[fancysubstring s ofs len alen] is equivalent to [fancystring (String.sub + s ofs len) alen]. *) val fancysubstring : string -> int -> int -> int -> document -(** [utf8string s] is a document that consists of the UTF-8-encoded string [s]. - This string must not contain a newline. *) +(**[utf8string s] is an atomic document that consists of the UTF-8-encoded + string [s]. This string must not contain a newline. [utf8string s] is + equivalent to [fancystring s (utf8_length s)], where [utf8_length s] is + the apparent length of the UTF-8-encoded string [s]. *) val utf8string: string -> document -(** [hardline] is a forced newline document. This document forces all enclosing - groups to be printed in non-flattening mode. In other words, any enclosing - groups are dissolved. *) +(** [utf8format format ...] is equivalent to + [utf8string (Printf.sprintf format ...)]. *) +val utf8format: ('a, unit, string, document) format4 -> 'a + +(** {2 Blanks and Newlines} *) + +(**The atomic document [hardline] represents a forced newline. This document + has infinite ideal width: thus, if there is a choice between printing it + in flat mode and printing it in normal mode, normal mode is preferred. In + other words, when [hardline] is placed directly inside a group, this + group is dissolved: [group hardline] is equivalent to [hardline]. This + combinator should be seldom used; consider using {!break} instead. *) val hardline: document -(** [blank n] is a document that consists of [n] blank characters. *) +(**The atomic document [blank n] consists of [n] blank characters. A blank + character is like an ordinary ASCII space character [char ' '], except + that blank characters that appear at the end of a line are automatically + suppressed. *) val blank: int -> document -(** [break n] is a document which consists of either [n] blank characters, - when forced to display on a single line, or a single newline character, - otherwise. Note that there is no choice at this point: choices are encoded - by the [group] combinator. *) +(**[space] is a synonym for [blank 1]. It consists of one blank character. + It is therefore not equivalent to [char ' ']. *) +val space: document + +(**The document [break n] is a breakable blank of width [n]. It produces [n] + blank characters if the printing engine is in flat mode, and a single + newline character if the printing engine is in normal mode. [break 1] is + equivalent to [ifflat (blank 1) hardline]. *) val break: int -> document -(** [doc1 ^^ doc2] is the concatenation of the documents [doc1] and [doc2]. *) -val (^^): document -> document -> document +(** {2 Composite Documents} *) -(** [nest j doc] is the document [doc], in which the indentation level has - been increased by [j], that is, in which [j] blanks have been inserted - after every newline character. Read this again: indentation is inserted - after every newline character. No indentation is inserted at the beginning - of the document. *) -val nest: int -> document -> document +(**[doc1 ^^ doc2] is the concatenation of the documents [doc1] and [doc2]. *) +val (^^): document -> document -> document -(** [group doc] encodes a choice. If possible, then the entire document [group - doc] is rendered on a single line. Otherwise, the group is dissolved, and - [doc] is rendered. There might be further groups within [doc], whose - presence will lead to further choices being explored. *) +(**[group doc] encodes a choice. If the document [doc] fits on the current + line, then it is rendered on a single line, in flat mode. (All [group] + combinators inside it are then ignored.) Otherwise, this group is + dissolved, and [doc] is rendered in normal mode. There might be more + groups within [doc], whose presence leads to further choices being + explored. *) val group: document -> document -(** [ifflat doc1 doc2] is rendered as [doc1] if part of a group that can be - successfully flattened, and is rendered as [doc2] otherwise. Use this - operation with caution. Because the pretty-printer is free to choose - between [doc1] and [doc2], these documents should be semantically - equivalent. *) +(**[ifflat doc1 doc2] is rendered as [doc1] if the printing engine is in + flat mode, that is, if the printing engine has determined that some + enclosing group fits on the current line. Otherwise, it is rendered as + [doc2]. Use this combinator with caution! Because the printing engine is + free to choose between [doc1] and [doc2], these documents must be + semantically equivalent. It is up to the user to enforce this property. *) val ifflat: document -> document -> document -(** [align doc] is the document [doc], in which the indentation level has been - set to the current column. Thus, [doc] is rendered within a box whose - upper left corner is the current position. *) +(**To render the document [nest j doc], the printing engine temporarily + increases the current indentation level by [j], then renders [doc]. The + effect of the current indentation level is as follows: every time a + newline character is emitted, it is immediately followed by [n] blank + characters, where [n] is the current indentation level. Thus, one may + think of [nest j doc] roughly as the document [doc] in which [j] blank + characters have been inserted after every newline character. *) +val nest: int -> document -> document + +(**To render [align doc], the printing engine sets the current indentation + level to the current column, then renders [doc]. In other words, the + document [doc] is rendered within a box whose upper left corner is the + current position of the printing engine. *) val align: document -> document -(** {1 Rendering documents} *) +(**A point is a pair of a line number and a column number. *) +type point = + int * int + +(**A range is a pair of points. *) +type range = + point * point + +(**The document [range hook doc] is printed like the document [doc], but + allows the caller to register a hook that is applied, when the document + is printed, to the range occupied by this document in the output text. + This offers a way of mapping positions in the output text back to + (sub)documents. *) +val range: (range -> unit) -> document -> document + +(** {1:rendering Rendering Documents} *) + +(**Three renderers are available. They offer the same API, described + by the signature {!RENDERER}, and differ only in the nature of the + output channel that they use. *) + +(**This signature describes the document renderers in a manner that + is independent of the type of the output channel. *) +module type RENDERER = sig + + (**The type of the output channel. *) + type channel -(** This renderer sends its output into an output channel. *) -module ToChannel : PPrintRenderer.RENDERER + (**The type of documents. *) + type document + + (** [pretty rfrac width channel document] pretty-prints the document + [document] into the output channel [channel]. The parameter [width] is + the maximum number of characters per line. The parameter [rfrac] is the + ribbon width, a fraction relative to [width]. The ribbon width is the + maximum number of non-indentation characters per line. *) + val pretty: float -> int -> channel -> document -> unit + + (** [compact channel document] prints the document [document] to the output + channel [channel]. No indentation is used. All newline instructions are + respected, that is, no groups are flattened. *) + val compact: channel -> document -> unit + +end + +(**This renderer sends its output into an output channel. *) +module ToChannel : RENDERER with type channel = out_channel and type document = document -(** This renderer sends its output into a memory buffer. *) -module ToBuffer : PPrintRenderer.RENDERER +(**This renderer sends its output into a memory buffer. *) +module ToBuffer : RENDERER with type channel = Buffer.t and type document = document -(** This renderer sends its output into a formatter channel. *) -module ToFormatter : PPrintRenderer.RENDERER +(**This renderer sends its output into a formatter channel. *) +module ToFormatter : RENDERER with type channel = Format.formatter and type document = document -(** {1 Defining custom documents} *) +(** {1:defining Defining Custom Documents} *) -(** A width requirement is expressed as an integer, where the value [max_int] - is reserved and represents infinity. *) +(**It is possible to define custom document constructors, provided they meet + the expectations of the printing engine. In short, the custom document + combinator {!val-custom} expects an object of class {!class-type-custom}. + This object must provide three methods. The method [requirement] must + compute the ideal width of the custom document. The methods [pretty] and + [compact] must render the custom document. For this purpose, they have + access to the {{!output}output channel} and to the {{!state}state} of the + printing engine. *) +(** A width requirement is expressed as an integer. The value [max_int] + is reserved and represents infinity. *) type requirement = int -val infinity : requirement -(** An output channel is represented abstractly as an object equipped with - methods for displaying one character and for displaying a substring. *) +(**[infinity] represents an infinite width requirement. *) +val infinity : requirement +(**An output channel is abstractly represented as an object equipped with + methods for displaying one character and for displaying a substring. *) class type output = object - (** [char c] sends the character [c] to the output channel. *) + (**[char c] sends the character [c] to the output channel. *) method char: char -> unit - (** [substring s ofs len] sends the substring of [s] delimited by the - offset [ofs] and the length [len] to the output channel. *) + (**[substring s ofs len] sends the substring of [s] delimited by the + offset [ofs] and the length [len] to the output channel. *) method substring: string -> int (* offset *) -> int (* length *) -> unit end -(** The rendering engine maintains the following internal state. Its structure - is subject to change in future versions of the library. Nevertheless, it is - exposed to the user who wishes to define custom documents. *) - +(**The internal state of the rendering engine is exposed to the user who + wishes to define custom documents. However, its structure is subject to + change in future versions of the library. *) type state = { width: int; @@ -157,10 +233,14 @@ type state = { mutable last_indent: int; (** The number of blanks that were printed at the beginning of the current - line. This field is updated (only) by the function [emit_hardline]. It - is used (only) to determine whether the ribbon width constraint is + line. This field is updated (only) when a hardline is emitted. It is + used (only) to determine whether the ribbon width constraint is respected. *) + mutable line: int; + (** The current line. This field is updated (only) when a hardline is + emitted. It is not used by the pretty-printing engine itself. *) + mutable column: int; (** The current column. This field must be updated whenever something is sent to the output channel. It is used (only) to determine whether the @@ -168,59 +248,60 @@ type state = { } -(** A custom document is defined by implementing the following methods. *) - +(**A custom document is defined by implementing an object of class + {!class-type-custom}. *) class type custom = object - (** A custom document must publish the width (i.e., the number of columns) - that it would like to occupy if it is printed on a single line (that is, - in flattening mode). The special value [infinity] means that this - document cannot be printed on a single line; this value causes any - groups that contain this document to be dissolved. This method should - in principle work in constant time. *) + (**A custom document must publish the width (i.e., the number of columns) + that it would like to occupy if printed on a single line (in flat + mode). The special value [infinity] means that this document cannot be + printed on a single line; this value causes any groups that contain + this document to be dissolved. This method should in principle work in + constant time. *) method requirement: requirement - (** The method [pretty] is used by the main rendering algorithm. It has - access to the output channel and to the algorithm's internal state, as - described above. In addition, it receives the current indentation level - and the current flattening mode (on or off). If flattening mode is on, - then the document must be printed on a single line, in a manner that is - consistent with the requirement that was published ahead of time. If - flattening mode is off, then there is no such obligation. The state must - be updated in a manner that is consistent with what is sent to the - output channel. *) + (**The method [pretty] is used by the main rendering algorithm. It has + access to the output channel and to the printing engine's internal + state. In addition, it receives the current indentation level and a + Boolean flag that tells whether the engine is currently in flat mode. + If the engine is in flat mode, then the document must be printed on a + single line, in a manner that is consistent with the width requirement + that was published ahead of time. If the engine is in normal mode, then + there is no such obligation. The state must be updated in a manner that + is consistent with what is sent to the output channel. *) method pretty: output -> state -> int -> bool -> unit - (** The method [compact] is used by the compact rendering algorithm. It has - access to the output channel only. *) + (**The method [compact] is used by the compact rendering algorithm. It + has access to the output channel only. *) method compact: output -> unit end -(** The function [custom] constructs a custom document. In other words, it - converts an object of type [custom] to a document. *) +(**[custom] constructs a custom document out an object of type + {!class-type-custom}. *) val custom: custom -> document -(** The key functions of the library are exposed, in the hope that they may be - useful to authors of custom (leaf and non-leaf) documents. In the case of - a leaf document, they can help perform certain basic functions; for - instance, applying the function [pretty] to the document [hardline] is a - simple way of printing a hardline, while respecting the indentation - parameters and updating the state in a correct manner. Similarly, applying - [pretty] to the document [blank n] is a simple way of printing [n] spaces. - In the case of a non-leaf document (i.e., one which contains - sub-documents), these functions are essential: they allow computing the - width requirement of a sub-document and displaying a sub-document. *) - -(** [requirement doc] computes the width requirement of the document [doc]. - It works in constant time. *) +(**Some of the key functions of the library are exposed, in the hope that + they may be useful to authors of custom (leaf and composite) documents. + In the case of a leaf document, they can help perform certain basic + functions; for instance, applying the function {!pretty} to the document + {!hardline} is a simple way of printing a hardline, while respecting the + indentation parameters and updating the state in a correct manner. + Similarly, applying {!pretty} to the document [blank n] is a simple way + of printing [n] blank characters. In the case of a composite document + (one that contains subdocuments), these functions are essential: they + allow computing the width requirement of a subdocument and displaying a + subdocument. *) + +(**[requirement doc] computes the width requirement of the document [doc]. + It runs in constant time. *) val requirement: document -> requirement -(** [pretty output state indent flatten doc] prints the document [doc]. See - the documentation of the method [pretty]. *) +(**[pretty output state indent flatten doc] prints the document [doc]. See + the documentation of the method [pretty] in the class + {!class-type-custom}. *) val pretty: output -> state -> int -> bool -> document -> unit -(** [compact output doc] prints the document [doc]. See the documentation of - the method [compact]. *) +(**[compact output doc] prints the document [doc]. See the documentation of + the method [compact] in the class {!class-type-custom}. *) val compact: output -> document -> unit - diff --git a/tests/menhir_tests/test10menhir_with_aux_args/PPrintRenderer.ml b/tests/menhir_tests/test10menhir_with_aux_args/PPrintRenderer.ml deleted file mode 100644 index 3449d6c..0000000 --- a/tests/menhir_tests/test10menhir_with_aux_args/PPrintRenderer.ml +++ /dev/null @@ -1,37 +0,0 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) - -(** A common signature for the multiple document renderers proposed by {!PPrintEngine}. *) - -module type RENDERER = sig - - (** Output channels. *) - type channel - - (** Documents. *) - type document - - (** [pretty rfrac width channel document] pretty-prints the document - [document] into the output channel [channel]. The parameter [width] is - the maximum number of characters per line. The parameter [rfrac] is the - ribbon width, a fraction relative to [width]. The ribbon width is the - maximum number of non-indentation characters per line. *) - val pretty: float -> int -> channel -> document -> unit - - (** [compact channel document] prints the document [document] to the output - channel [channel]. No indentation is used. All newline instructions are - respected, that is, no groups are flattened. *) - val compact: channel -> document -> unit - -end - diff --git a/tests/menhir_tests/test10menhir_with_aux_args/PPrintCombinators.ml b/tests/menhir_tests/test10menhir_with_aux_rules/PPrint.ml similarity index 61% rename from tests/menhir_tests/test10menhir_with_aux_args/PPrintCombinators.ml rename to tests/menhir_tests/test10menhir_with_aux_rules/PPrint.ml index 7049987..da55896 100644 --- a/tests/menhir_tests/test10menhir_with_aux_args/PPrintCombinators.ml +++ b/tests/menhir_tests/test10menhir_with_aux_rules/PPrint.ml @@ -1,19 +1,19 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) - -open PPrintEngine - -(* ------------------------------------------------------------------------- *) +(******************************************************************************) +(* *) +(* PPrint *) +(* *) +(* François Pottier, Inria Paris *) +(* Nicolas Pouillard *) +(* *) +(* Copyright 2007-2022 Inria. All rights reserved. This file is *) +(* distributed under the terms of the GNU Library General Public *) +(* License, with an exception, as described in the file LICENSE. *) +(* *) +(******************************************************************************) + +include PPrintEngine + +(* -------------------------------------------------------------------------- *) (* Predefined single-character documents. *) @@ -31,7 +31,6 @@ let bquote = char '`' let semi = char ';' let colon = char ':' let comma = char ',' -let space = char ' ' let dot = char '.' let sharp = char '#' let slash = char '/' @@ -51,7 +50,7 @@ let underscore = char '_' let bang = char '!' let bar = char '|' -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Repetition. *) @@ -67,7 +66,7 @@ let repeat n doc = in loop n doc empty -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Delimiters. *) @@ -83,7 +82,7 @@ let parens = enclose lparen rparen let angles = enclose langle rangle let brackets = enclose lbracket rbracket -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Some functions on lists. *) @@ -97,7 +96,7 @@ let foldli (f : int -> 'b -> 'a -> 'b) (accu : 'b) (xs : 'a list) : 'b = f i accu x ) accu xs -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Working with lists of documents. *) @@ -147,7 +146,7 @@ let optional f = function | Some x -> f x -(* ------------------------------------------------------------------------- *) +(* -------------------------------------------------------------------------- *) (* Text. *) @@ -210,7 +209,7 @@ let words s = let n = String.length s in (* A two-state finite automaton. *) (* In this state, we have skipped at least one blank character. *) - let rec skipping accu i = + let rec skipping accu i = if i = n then (* There was whitespace at the end. Drop it. *) accu @@ -235,7 +234,7 @@ let words s = | '\n' | '\r' -> (* A new word has been identified. *) - let accu = substring s i (j - i) :: accu in + let accu = substring s i (j - i) :: accu in skipping accu (j + 1) | _ -> (* Continue inside the current word. *) @@ -252,7 +251,7 @@ let flow_map sep f docs = (* This idiom allows beginning a new line if [doc] does not fit on the current line. *) group (sep ^^ f doc) - ) empty docs + ) empty docs let flow sep docs = flow_map sep (fun x -> x) docs @@ -260,8 +259,7 @@ let flow sep docs = let url s = flow (break 0) (split (function '/' | '.' -> true | _ -> false) s) -(* ------------------------------------------------------------------------- *) - +(* -------------------------------------------------------------------------- *) (* Alignment and indentation. *) let hang i d = @@ -281,11 +279,6 @@ let (^//^) = let jump n b y = group (nest n (break b ^^ y)) -(* Deprecated. -let ( ^@^ ) x y = group (x ^/^ y) -let ( ^@@^ ) x y = group (nest 2 (x ^/^ y)) -*) - let infix n b op x y = prefix n b (x ^^ blank b ^^ op) y @@ -309,3 +302,167 @@ let surround_separate_map n b void opening sep closing f xs = | _ :: _ -> surround n b opening (separate_map sep f xs) closing +(* -------------------------------------------------------------------------- *) +(* Printing OCaml values. *) + +module OCaml = struct + +open Printf + +type constructor = string +type type_name = string +type record_field = string +type tag = int + +(* -------------------------------------------------------------------------- *) + +(* This internal [sprintf]-like function produces a document. We use [string], + as opposed to [arbitrary_string], because the strings that we produce will + never contain a newline character. *) + +let dsprintf format = + ksprintf string format + +(* -------------------------------------------------------------------------- *) + +(* Nicolas prefers using this code as opposed to just [sprintf "%g"] or + [sprintf "%f"]. The latter print [inf] and [-inf], whereas OCaml + understands [infinity] and [neg_infinity]. [sprintf "%g"] does not add a + trailing dot when the number happens to be an integral number. [sprintf + "%F"] seems to lose precision and ignores the precision modifier. *) + +let valid_float_lexeme (s : string) : string = + let l = String.length s in + let rec loop i = + if i >= l then + (* If we reach the end of the string and have found only characters in + the set '0' .. '9' and '-', then this string will be considered as an + integer literal by OCaml. Adding a trailing dot makes it a float + literal. *) + s ^ "." + else + match s.[i] with + | '0' .. '9' | '-' -> loop (i + 1) + | _ -> s + in loop 0 + +(* This function constructs a string representation of a floating point + number. This representation is supposed to be accepted by OCaml as a + valid floating point literal. *) + +let float_representation (f : float) : string = + match classify_float f with + | FP_nan -> + "nan" + | FP_infinite -> + if f < 0.0 then "neg_infinity" else "infinity" + | _ -> + (* Try increasing precisions and validate. *) + let s = sprintf "%.12g" f in + if f = float_of_string s then valid_float_lexeme s else + let s = sprintf "%.15g" f in + if f = float_of_string s then valid_float_lexeme s else + sprintf "%.18g" f + +(* -------------------------------------------------------------------------- *) + +(* A few constants and combinators, used below. *) + +let some = + string "Some" + +let none = + string "None" + +let lbracketbar = + string "[|" + +let rbracketbar = + string "|]" + +let seq1 opening separator closing = + surround_separate 2 0 + (opening ^^ closing) opening (separator ^^ break 1) closing + +let seq2 opening separator closing = + surround_separate_map 2 1 + (opening ^^ closing) opening (separator ^^ break 1) closing + +(* -------------------------------------------------------------------------- *) + +(* The following functions are printers for many types of OCaml values. *) + +(* There is no protection against cyclic values. *) + +let tuple = + seq1 lparen comma rparen + +let variant _ cons _ args = + match args with + | [] -> + !^cons + | _ :: _ -> + !^cons ^^ tuple args + +let record _ fields = + seq2 lbrace semi rbrace (fun (k, v) -> infix 2 1 equals !^k v) fields + +let option f = function + | None -> + none + | Some x -> + some ^^ tuple [f x] + +let list f xs = + seq2 lbracket semi rbracket f xs + +let flowing_list f xs = + group (lbracket ^^ space ^^ nest 2 ( + flow_map (semi ^^ break 1) f xs + ) ^^ space ^^ rbracket) + +let array f xs = + seq2 lbracketbar semi rbracketbar f (Array.to_list xs) + +let flowing_array f xs = + group (lbracketbar ^^ space ^^ nest 2 ( + flow_map (semi ^^ break 1) f (Array.to_list xs) + ) ^^ space ^^ rbracketbar) + +let ref f x = + record "ref" ["contents", f !x] + +let float f = + string (float_representation f) + +let int = + dsprintf "%d" + +let int32 = + dsprintf "%ld" + +let int64 = + dsprintf "%Ld" + +let nativeint = + dsprintf "%nd" + +let char = + dsprintf "%C" + +let bool = + dsprintf "%B" + +let unit = + dsprintf "()" + +let string = + dsprintf "%S" + +let unknown tyname _ = + dsprintf "" tyname + +type representation = + document + +end (* OCaml *) diff --git a/tests/menhir_tests/test10menhir_with_aux_rules/PPrint.mli b/tests/menhir_tests/test10menhir_with_aux_rules/PPrint.mli new file mode 100644 index 0000000..89eeeeb --- /dev/null +++ b/tests/menhir_tests/test10menhir_with_aux_rules/PPrint.mli @@ -0,0 +1,345 @@ +(******************************************************************************) +(* *) +(* PPrint *) +(* *) +(* François Pottier, Inria Paris *) +(* Nicolas Pouillard *) +(* *) +(* Copyright 2007-2022 Inria. All rights reserved. This file is *) +(* distributed under the terms of the GNU Library General Public *) +(* License, with an exception, as described in the file LICENSE. *) +(* *) +(******************************************************************************) + +include module type of PPrintEngine (** @inline *) + +(** {1:combinators High-Level Combinators} *) + +(** {2 Single Characters} *) + +(**The following atomic documents consist of a single character. Each of + them is a synonym for the application of {!char} to some constant + character. For instance, {!lparen} is a synonym for [char '(']. *) + +val lparen: document +val rparen: document +val langle: document +val rangle: document +val lbrace: document +val rbrace: document +val lbracket: document +val rbracket: document +val squote: document +val dquote: document +val bquote: document +val semi: document +val colon: document +val comma: document +val dot: document +val sharp: document +val slash: document +val backslash: document +val equals: document +val qmark: document +val tilde: document +val at: document +val percent: document +val dollar: document +val caret: document +val ampersand: document +val star: document +val plus: document +val minus: document +val underscore: document +val bang: document +val bar: document + +(** {2 Delimiters} *) + +(**[precede l x] is [l ^^ x]. *) +val precede: document -> document -> document + +(**[terminate r x] is [x ^^ r]. *) +val terminate: document -> document -> document + +(**[enclose l r x] is [l ^^ x ^^ r]. *) +val enclose: document -> document -> document -> document + +(**The following combinators enclose a document within a pair of delimiters. + They are partial applications of [enclose]. No whitespace or line break + is introduced. *) + +val squotes: document -> document +val dquotes: document -> document +val bquotes: document -> document +val braces: document -> document +val parens: document -> document +val angles: document -> document +val brackets: document -> document + +(** {2 Repetition} *) + +(**[twice doc] is the document obtained by concatenating two copies of + the document [doc]. *) +val twice: document -> document + +(**[repeat n doc] is the document obtained by concatenating [n] copies of + the document [doc]. *) +val repeat: int -> document -> document + +(** {2 Lists and Options} *) + +(**[concat docs] is the concatenation of the documents in the list [docs]. *) +val concat: document list -> document + +(**[separate sep docs] is the concatenation of the documents in the list + [docs]. The separator [sep] is inserted between every two adjacent + documents. *) +val separate: document -> document list -> document + +(**[concat_map f xs] is equivalent to [concat (List.map f xs)]. *) +val concat_map: ('a -> document) -> 'a list -> document + +(**[separate_map sep f xs] is equivalent to [separate sep (List.map f xs)]. *) +val separate_map: document -> ('a -> document) -> 'a list -> document + +(**[separate2 sep last_sep docs] is the concatenation of the documents in + the list [docs]. The separator [sep] is inserted between every two + adjacent documents, except between the last two documents, where the + separator [last_sep] is used instead. *) +val separate2: document -> document -> document list -> document + +(**[optional f None] is the empty document. [optional f (Some x)] is + the document [f x]. *) +val optional: ('a -> document) -> 'a option -> document + +(** {2 Text} *) + +(**[lines s] is the list of documents obtained by splitting [s] at newline + characters, and turning each line into a document via [substring]. This + code is not UTF-8 aware. *) +val lines: string -> document list + +(**[arbitrary_string s] is equivalent to [separate (break 1) (lines s)]. + It is analogous to [string s], but is valid even if the string [s] + contains newline characters. *) +val arbitrary_string: string -> document + +(**[words s] is the list of documents obtained by splitting [s] at whitespace + characters, and turning each word into a document via [substring]. All + whitespace is discarded. This code is not UTF-8 aware. *) +val words: string -> document list + +(**[split ok s] splits the string [s] before and after every occurrence of a + character that satisfies the predicate [ok]. The substrings thus obtained + are turned into documents, and a list of documents is returned. No + information is lost: the concatenation of the documents yields the + original string. This code is not UTF-8 aware. *) +val split: (char -> bool) -> string -> document list + +(**[flow sep docs] separates the documents in the list [docs] with the + separator [sep] and arranges for a new line to begin whenever a document + does not fit on the current line. This is useful for typesetting + free-flowing, ragged-right text. A typical choice of [sep] is [break b], + where [b] is the number of spaces that must be inserted between two + consecutive words (when displayed on the same line). *) +val flow: document -> document list -> document + +(**[flow_map sep f docs] is equivalent to [flow sep (List.map f docs)]. *) +val flow_map: document -> ('a -> document) -> 'a list -> document + +(**[url s] is a possible way of displaying the URL [s]. A potential line + break is inserted immediately before and immediately after every slash + and dot character. *) +val url: string -> document + +(** {2 Alignment and Indentation} *) + +(**[hang n doc] is analogous to [align], but additionally indents all lines, + except the first one, by [n]. Thus, the text in the box forms a hanging + indent. *) +val hang: int -> document -> document + +(**[prefix n b left right] has the following flat layout: + {[ + left right + ]} + and the following non-flat layout: + {[ + left + right + ]} + The parameter [n] controls the nesting of [right] (when not flat). + The parameter [b] controls the number of spaces between [left] and [right] + (when flat). *) +val prefix: int -> int -> document -> document -> document + +(**[jump n b right] is equivalent to [prefix n b empty right]. *) +val jump: int -> int -> document -> document + +(**[infix n b middle left right] has the following flat layout: + {[ + left middle right + ]} + and the following non-flat layout: + {[ + left middle + right + ]} + The parameter [n] controls the nesting of [right] (when not flat). + The parameter [b] controls the number of spaces between [left] and [middle] + (always) and between [middle] and [right] (when flat). *) +val infix: int -> int -> document -> document -> document -> document + +(**[surround n b opening contents closing] has the following flat layout: + {[ + opening contents closing + ]} + and the following non-flat layout: + {[ + opening + contents + closing + ]} + The parameter [n] controls the nesting of [contents] (when not flat). + The parameter [b] controls the number of spaces between [opening] and + [contents] and between [contents] and [closing] (when flat). +*) +val surround: int -> int -> document -> document -> document -> document + +(**[soft_surround] is analogous to [surround], but involves more than one + group, so it offers possibilities other than the completely flat layout + (where [opening], [contents], and [closing] appear on a single line) and + the completely developed layout (where [opening], [contents], and + [closing] appear on separate lines). It tries to place the beginning of + [contents] on the same line as [opening], and to place [closing] on the + same line as the end of [contents], if possible. *) +val soft_surround: int -> int -> document -> document -> document -> document + +(**[surround_separate n b void opening sep closing docs] is equivalent to + [surround n b opening (separate sep docs) closing], except when the list + [docs] is empty, in which case it reduces to [void]. *) +val surround_separate: + int -> int -> + document -> document -> document -> document -> + document list -> document + +(**[surround_separate_map n b void opening sep closing f xs] is equivalent + to [surround_separate n b void opening sep closing (List.map f xs)]. *) +val surround_separate_map: + int -> int -> + document -> document -> document -> document -> + ('a -> document) -> 'a list -> document + +(** {2 Short-Hands} *) + +(**[!^s] is a short-hand for [string s]. *) +val ( !^ ) : string -> document + +(**[x ^/^ y] separates [x] and [y] with a breakable space. + It is a short-hand for [x ^^ break 1 ^^ y]. *) +val ( ^/^ ) : document -> document -> document + +(**[x ^//^ y] is a short-hand for [prefix 2 1 x y]. *) +val ( ^//^ ) : document -> document -> document + +(** {1:ocaml Printing OCaml Values} *) + +(**This module offers document combinators that help print OCaml values. The + strings produced by rendering these documents are supposed to be accepted + by the OCaml parser as valid values. + + These functions do {i not} distinguish between mutable and immutable + values. They do {i not} recognize sharing, and do {i not} incorporate a + protection against cyclic values. *) +module OCaml : sig + +(* The signature of this module is compatible with that expected by the + [camlp4] generator [Camlp4RepresentationGenerator]. This explains why + some functions have unused parameters. This is also the reason why + there is a type [representation]. *) + +type constructor = string +type type_name = string +type record_field = string +type tag = int + +(**[variant _ dc _ args] represents a constructed value whose data + constructor is [dc] and whose arguments are [args]. The other two + parameters are presently unused. *) +val variant : type_name -> constructor -> tag -> document list -> document + +(**[record _ fields] represents a record value whose fields are [fields]. + The other parameter is presently unused. *) +val record : type_name -> (record_field * document) list -> document + +(**[tuple args] represents a tuple value whose components are [args]. *) +val tuple : document list -> document + +(**[string s] represents the literal string [s]. *) +val string : string -> document + +(**[int i] represents the literal integer [i]. *) +val int : int -> document + +(**[int32 i] represents the literal 32-bit integer [i]. *) +val int32 : int32 -> document + +(**[int64 i] represents the literal 64-bit integer [i]. *) +val int64 : int64 -> document + +(**[nativeint i] represents the literal native integer [i]. *) +val nativeint : nativeint -> document + +(**[float f] represents the literal floating-point number [f]. *) +val float : float -> document + +(**[char c] represents the literal character [c]. *) +val char : char -> document + +(**[bool b] represents the Boolean value [b]. *) +val bool : bool -> document + +(**[unit] represents the unit constant [()]. *) +val unit : document + +(**[option f o] represents the option [o]. The representation of the + element, if present, is computed by the function [f]. *) +val option : ('a -> document) -> 'a option -> document + +(**[list f xs] represents the list [xs]. The representation of each element + is computed by the function [f]. If the whole list fits on a single line, + then it is printed on a single line; otherwise each element is printed on + a separate line. *) +val list : ('a -> document) -> 'a list -> document + +(**[flowing_list f xs] represents the list [xs]. The representation of each + element is computed by the function [f]. As many elements are possible + are printed on each line. *) +val flowing_list : ('a -> document) -> 'a list -> document + +(**[array f xs] represents the array [xs]. The representation of each + element is computed by the function [f]. If the whole array fits on a + single line, then it is printed on a single line; otherwise each element + is printed on a separate line. *) +val array : ('a -> document) -> 'a array -> document + +(**[flowing_array f xs] represents the array [xs]. The representation of + each element is computed by the function [f]. As many elements are + possible are printed on each line. *) +val flowing_array : ('a -> document) -> 'a array -> document + +(**[ref r] represents the reference [r]. The representation of the content + is computed by the function [f]. *) +val ref : ('a -> document) -> 'a ref -> document + +(** [unknown t _] represents an unknown value of type [t]. It is rendered + as a string of the form []. *) +val unknown : type_name -> 'a -> document + +(**/**) + +type representation = + document + +end (* OCaml *) diff --git a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintCombinators.mli b/tests/menhir_tests/test10menhir_with_aux_rules/PPrintCombinators.mli deleted file mode 100644 index c538cb3..0000000 --- a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintCombinators.mli +++ /dev/null @@ -1,236 +0,0 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) - -open PPrintEngine - -(** A set of high-level combinators for building documents. *) - -(** {1 Single characters} *) - -(** The following constant documents consist of a single character. *) - -val lparen: document -val rparen: document -val langle: document -val rangle: document -val lbrace: document -val rbrace: document -val lbracket: document -val rbracket: document -val squote: document -val dquote: document -val bquote: document -val semi: document -val colon: document -val comma: document -val space: document -val dot: document -val sharp: document -val slash: document -val backslash: document -val equals: document -val qmark: document -val tilde: document -val at: document -val percent: document -val dollar: document -val caret: document -val ampersand: document -val star: document -val plus: document -val minus: document -val underscore: document -val bang: document -val bar: document - -(** {1 Delimiters} *) - -(** [precede l x] is [l ^^ x]. *) -val precede: document -> document -> document - -(** [terminate r x] is [x ^^ r]. *) -val terminate: document -> document -> document - -(** [enclose l r x] is [l ^^ x ^^ r]. *) -val enclose: document -> document -> document -> document - -(** The following combinators enclose a document within a pair of delimiters. - They are partial applications of [enclose]. No whitespace or line break is - introduced. *) - -val squotes: document -> document -val dquotes: document -> document -val bquotes: document -> document -val braces: document -> document -val parens: document -> document -val angles: document -> document -val brackets: document -> document - -(** {1 Repetition} *) - -(** [twice doc] is the document obtained by concatenating two copies of - the document [doc]. *) -val twice: document -> document - -(** [repeat n doc] is the document obtained by concatenating [n] copies of - the document [doc]. *) -val repeat: int -> document -> document - -(** {1 Lists and options} *) - -(** [concat docs] is the concatenation of the documents in the list [docs]. *) -val concat: document list -> document - -(** [separate sep docs] is the concatenation of the documents in the list - [docs]. The separator [sep] is inserted between every two adjacent - documents. *) -val separate: document -> document list -> document - -(** [concat_map f xs] is equivalent to [concat (List.map f xs)]. *) -val concat_map: ('a -> document) -> 'a list -> document - -(** [separate_map sep f xs] is equivalent to [separate sep (List.map f xs)]. *) -val separate_map: document -> ('a -> document) -> 'a list -> document - -(** [separate2 sep last_sep docs] is the concatenation of the documents in the - list [docs]. The separator [sep] is inserted between every two adjacent - documents, except between the last two documents, where the separator - [last_sep] is used instead. *) -val separate2: document -> document -> document list -> document - -(** [optional f None] is the empty document. [optional f (Some x)] is - the document [f x]. *) -val optional: ('a -> document) -> 'a option -> document - -(** {1 Text} *) - -(** [lines s] is the list of documents obtained by splitting [s] at newline - characters, and turning each line into a document via [substring]. This - code is not UTF-8 aware. *) -val lines: string -> document list - -(** [arbitrary_string s] is equivalent to [separate (break 1) (lines s)]. - It is analogous to [string s], but is valid even if the string [s] - contains newline characters. *) -val arbitrary_string: string -> document - -(** [words s] is the list of documents obtained by splitting [s] at whitespace - characters, and turning each word into a document via [substring]. All - whitespace is discarded. This code is not UTF-8 aware. *) -val words: string -> document list - -(** [split ok s] splits the string [s] before and after every occurrence of a - character that satisfies the predicate [ok]. The substrings thus obtained - are turned into documents, and a list of documents is returned. No - information is lost: the concatenation of the documents yields the - original string. This code is not UTF-8 aware. *) -val split: (char -> bool) -> string -> document list - -(** [flow sep docs] separates the documents in the list [docs] with the - separator [sep] and arranges for a new line to begin whenever a document - does not fit on the current line. This is useful for typesetting - free-flowing, ragged-right text. A typical choice of [sep] is [break b], - where [b] is the number of spaces that must be inserted between two - consecutive words (when displayed on the same line). *) -val flow: document -> document list -> document - -(** [flow_map sep f docs] is equivalent to [flow sep (List.map f docs)]. *) -val flow_map: document -> ('a -> document) -> 'a list -> document - -(** [url s] is a possible way of displaying the URL [s]. A potential line - break is inserted immediately before and immediately after every slash - and dot character. *) -val url: string -> document - -(** {1 Alignment and indentation} *) - -(* [hang n doc] is analogous to [align], but additionally indents - all lines, except the first one, by [n]. Thus, the text in the - box forms a hanging indent. *) -val hang: int -> document -> document - -(** [prefix n b left right] has the following flat layout: {[ -left right -]} -and the following non-flat layout: -{[ -left - right -]} -The parameter [n] controls the nesting of [right] (when not flat). -The parameter [b] controls the number of spaces between [left] and [right] -(when flat). - *) -val prefix: int -> int -> document -> document -> document - -(** [jump n b right] is equivalent to [prefix n b empty right]. *) -val jump: int -> int -> document -> document - -(** [infix n b middle left right] has the following flat layout: {[ -left middle right -]} -and the following non-flat layout: {[ -left middle - right -]} -The parameter [n] controls the nesting of [right] (when not flat). -The parameter [b] controls the number of spaces between [left] and [middle] -(always) and between [middle] and [right] (when flat). -*) -val infix: int -> int -> document -> document -> document -> document - -(** [surround n b opening contents closing] has the following flat layout: {[ -opening contents closing -]} -and the following non-flat layout: {[ -opening - contents -closing -]} -The parameter [n] controls the nesting of [contents] (when not flat). -The parameter [b] controls the number of spaces between [opening] and [contents] -and between [contents] and [closing] (when flat). -*) -val surround: int -> int -> document -> document -> document -> document - -(** [soft_surround] is analogous to [surround], but involves more than one - group, so it offers possibilities other than the completely flat layout - (where [opening], [contents], and [closing] appear on a single line) and - the completely developed layout (where [opening], [contents], and - [closing] appear on separate lines). It tries to place the beginning of - [contents] on the same line as [opening], and to place [closing] on the - same line as the end of [contents], if possible. -*) -val soft_surround: int -> int -> document -> document -> document -> document - -(** [surround_separate n b void opening sep closing docs] is equivalent to - [surround n b opening (separate sep docs) closing], except when the - list [docs] is empty, in which case it reduces to [void]. *) -val surround_separate: int -> int -> document -> document -> document -> document -> document list -> document - -(** [surround_separate_map n b void opening sep closing f xs] is equivalent to - [surround_separate n b void opening sep closing (List.map f xs)]. *) -val surround_separate_map: int -> int -> document -> document -> document -> document -> ('a -> document) -> 'a list -> document - -(** {1 Short-hands} *) - -(** [!^s] is a short-hand for [string s]. *) -val ( !^ ) : string -> document - -(** [x ^/^ y] separates [x] and [y] with a breakable space. - It is a short-hand for [x ^^ break 1 ^^ y]. *) -val ( ^/^ ) : document -> document -> document - -(** [x ^//^ y] is a short-hand for [prefix 2 1 x y]. *) -val ( ^//^ ) : document -> document -> document - diff --git a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.ml b/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.ml index 2a78363..3131893 100644 --- a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.ml +++ b/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.ml @@ -1,15 +1,23 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) +(******************************************************************************) +(* *) +(* PPrint *) +(* *) +(* François Pottier, Inria Paris *) +(* Nicolas Pouillard *) +(* *) +(* Copyright 2007-2022 Inria. All rights reserved. This file is *) +(* distributed under the terms of the GNU Library General Public *) +(* License, with an exception, as described in the file LICENSE. *) +(* *) +(******************************************************************************) + +(** A point is a pair of a line number and a column number. *) +type point = + int * int + +(** A range is a pair of points. *) +type range = + point * point (* ------------------------------------------------------------------------- *) @@ -51,6 +59,82 @@ class type output = object end +(* ------------------------------------------------------------------------- *) + +(* Printing blank space. This is used both internally (to emit indentation + characters) and via the public combinator [blank]. *) + +let blank_length = + 80 + +let blank_buffer = + String.make blank_length ' ' + +let rec blanks (output : output) n = + if n <= 0 then + () + else if n <= blank_length then + output#substring blank_buffer 0 n + else begin + output#substring blank_buffer 0 blank_length; + blanks output (n - blank_length) + end + +(* ------------------------------------------------------------------------- *) + +(* The class [buffering] implements a wrapper that delays the printing of + blank characters. This includes indentation characters and characters + produced by the combinator [blank]. The printing of these characters is + delayed until it is known that they are followed by something on the same + line; if they are not followed with anything, then it is canceled. + + The actual printing task is delegated to the object [delegate], whose type + is [output]; the new object has type [output] as well. *) + +class buffering (delegate : output) : output = object (self) + + (* The number of blank characters that are withholding. *) + val mutable buffered = 0 + + (* [flush] sends out the blank characters that have been withheld. *) + method private flush = + blanks delegate buffered; + buffered <- 0 + + method char c : unit = + begin match c with + | '\n' -> + (* The current line ends here. Any blank characters that were withheld + are destroyed. This is where we avoid printing blank characters if + nothing follows them. *) + buffered <- 0 + | _ -> + (* The current line is nonempty. Any blank characters that were + withheld can now be flushed. *) + self#flush + end; + (* Print this character as usual. *) + delegate#char c + + method substring s pos len = + (* If this is a string of length zero, then there is nothing to do. *) + if len = 0 then + () + (* If this is a blank string (which we recognize by its address), then + its content is withheld. *) + else if s == blank_buffer then + buffered <- buffered + len + (* If this is not a blank string, then the blank characters that were + withheld up to this point can now be flushed. *) + else begin + self#flush; + delegate#substring s pos len + end + +end + +(* ------------------------------------------------------------------------- *) + (* Three kinds of output channels are wrapped so as to satisfy the above interface: OCaml output channels, OCaml memory buffers, and OCaml formatters. *) @@ -72,8 +156,17 @@ class buffer_output buffer = object end class formatter_output fmt = object - method char = Format.pp_print_char fmt - method substring = fst (Format.pp_get_formatter_output_functions fmt ()) + method char = function + | '\n' -> Format.pp_force_newline fmt () + | ' ' -> Format.pp_print_space fmt () + | c -> Format.pp_print_char fmt c + + method substring str ofs len = + Format.pp_print_text fmt ( + if ofs = 0 && len = String.length str + then str + else String.sub str ofs len + ) end (* ------------------------------------------------------------------------- *) @@ -94,10 +187,14 @@ type state = { mutable last_indent: int; (** The number of blanks that were printed at the beginning of the current - line. This field is updated (only) by the function [emit_hardline]. It - is used (only) to determine whether the ribbon width constraint is + line. This field is updated (only) when a hardline is emitted. It is + used (only) to determine whether the ribbon width constraint is respected. *) + mutable line: int; + (** The current line. This field is updated (only) when a hardline is + emitted. It is not used by the pretty-printing engine itself. *) + mutable column: int; (** The current column. This field must be updated whenever something is sent to the output channel. It is used (only) to determine whether the @@ -113,6 +210,7 @@ let initial rfrac width = { width = width; ribbon = max 0 (min width (truncate (float_of_int width *. rfrac))); last_indent = 0; + line = 0; column = 0 } @@ -245,6 +343,12 @@ type document = | Align of requirement * document + (* [Range (req, hook, doc)] is printed like [doc]. After it is printed, the + function [hook] is applied to the range that is occupied by [doc] in the + output. *) + + | Range of requirement * (range -> unit) * document + (* [Custom (req, f)] is a document whose appearance is user-defined. *) | Custom of custom @@ -276,7 +380,8 @@ let rec requirement = function | Cat (req, _, _) | Nest (req, _, _) | Group (req, _) - | Align (req, _) -> + | Align (req, _) + | Range (req, _, _) -> (* These nodes store their requirement -- which is computed when the node is constructed -- so as to allow us to answer in constant time here. *) @@ -298,7 +403,7 @@ let char c = Char c let space = - char ' ' + Blank 1 let string s = String s @@ -332,6 +437,9 @@ let utf8_length s = let utf8string s = fancystring s (utf8_length s) +let utf8format f = + Printf.ksprintf utf8string f + let hardline = HardLine @@ -339,8 +447,6 @@ let blank n = match n with | 0 -> empty - | 1 -> - space | _ -> Blank n @@ -394,6 +500,9 @@ let group x = let align x = Align (requirement x, x) +let range hook x = + Range (requirement x, hook, x) + let custom c = (* Sanity check. *) assert (c#requirement >= 0); @@ -401,26 +510,6 @@ let custom c = (* ------------------------------------------------------------------------- *) -(* Printing blank space (indentation characters). *) - -let blank_length = - 80 - -let blank_buffer = - String.make blank_length ' ' - -let rec blanks output n = - if n <= 0 then - () - else if n <= blank_length then - output#substring blank_buffer 0 n - else begin - output#substring blank_buffer 0 blank_length; - blanks output (n - blank_length) - end - -(* ------------------------------------------------------------------------- *) - (* This function expresses the following invariant: if we are in flattening mode, then we must be within bounds, i.e. the width and ribbon width constraints must be respected. *) @@ -445,12 +534,14 @@ let ok state flatten : bool = manner in which the document is rendered. *) (* The code is written in tail-recursive style, so as to avoid running out of - stack space if the document is very deep. Its explicit continuation can be - viewed as a sequence of pending calls to [pretty]. *) + stack space if the document is very deep. Each [KCons] cell in a + continuation represents a pending call to [pretty]. Each [KRange] cell + represents a pending call to a user-provided range hook. *) type cont = | KNil | KCons of int * bool * document * cont + | KRange of (range -> unit) * point * cont let rec pretty (output : output) @@ -498,6 +589,7 @@ let rec pretty (* Emit a hardline. *) output#char '\n'; blanks output indent; + state.line <- state.line + 1; state.column <- indent; state.last_indent <- indent; (* Continue. *) @@ -537,6 +629,10 @@ let rec pretty (* assert (state.column > state.last_indent); *) pretty output state state.column flatten doc cont + | Range (_, hook, doc) -> + let start : point = (state.line, state.column) in + pretty output state indent flatten doc (KRange (hook, start, cont)) + | Custom c -> (* Invoke the document's custom rendering function. *) c#pretty output state indent flatten; @@ -550,6 +646,10 @@ and continue output state = function () | KCons (indent, flatten, doc, cont) -> pretty output state indent flatten doc cont + | KRange (hook, start, cont) -> + let finish : point = (state.line, state.column) in + hook (start, finish); + continue output state cont (* Publish a version of [pretty] that does not take an explicit continuation. This function may be used by authors of custom documents. We do not expose @@ -575,7 +675,7 @@ let rec compact output doc cont = let len = String.length s in output#substring s 0 len; continue output cont - | FancyString (s, ofs, len, apparent_length) -> + | FancyString (s, ofs, len, _apparent_length) -> output#substring s ofs len; continue output cont | Blank n -> @@ -589,7 +689,8 @@ let rec compact output doc cont = | IfFlat (doc, _) | Nest (_, _, doc) | Group (_, doc) - | Align (_, doc) -> + | Align (_, doc) + | Range (_, _, doc) -> compact output doc cont | Custom c -> (* Invoke the document's custom rendering function. *) @@ -612,13 +713,21 @@ let compact output doc = (* This is just boilerplate. *) +module type RENDERER = sig + type channel + type document + val pretty: float -> int -> channel -> document -> unit + val compact: channel -> document -> unit +end + module MakeRenderer (X : sig type channel val output: channel -> output -end) = struct +end) +: RENDERER with type channel = X.channel and type document = document += struct type channel = X.channel - type dummy = document - type document = dummy + type nonrec document = document let pretty rfrac width channel doc = pretty (X.output channel) (initial rfrac width) 0 false doc let compact channel doc = compact (X.output channel) doc end @@ -626,17 +735,17 @@ end module ToChannel = MakeRenderer(struct type channel = out_channel - let output = new channel_output + let output channel = new buffering (new channel_output channel) end) module ToBuffer = MakeRenderer(struct type channel = Buffer.t - let output = new buffer_output + let output buffer = new buffering (new buffer_output buffer) end) module ToFormatter = MakeRenderer(struct type channel = Format.formatter - let output = new formatter_output + let output fmt = new buffering (new formatter_output fmt) end) diff --git a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.mli b/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.mli index eda61a6..4d03b12 100644 --- a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.mli +++ b/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.mli @@ -1,150 +1,226 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) - -(** A pretty-printing engine and a set of basic document combinators. *) - -(** {1 Building documents} *) - -(** Documents must be built in memory before they are rendered. This may seem - costly, but it is a simple approach, and works well. *) - -(** The following operations form a set of basic (low-level) combinators for - building documents. On top of these combinators, higher-level combinators - can be defined: see {!PPrintCombinators}. *) - -(** This is the abstract type of documents. *) +(******************************************************************************) +(* *) +(* PPrint *) +(* *) +(* François Pottier, Inria Paris *) +(* Nicolas Pouillard *) +(* *) +(* Copyright 2007-2022 Inria. All rights reserved. This file is *) +(* distributed under the terms of the GNU Library General Public *) +(* License, with an exception, as described in the file LICENSE. *) +(* *) +(******************************************************************************) + +(**[PPrint] is an OCaml library for {b pretty-printing textual documents}. + It takes care of {b indentation and line breaks}, and is typically used + to {b pretty-print code}. *) + +(** {1:building Building Documents} *) + +(**The abstract type of documents. *) type document -(** The following basic (low-level) combinators allow constructing documents. *) +(** {2 Atomic Documents} *) -(** [empty] is the empty document. *) +(**[empty] is the empty document. *) val empty: document -(** [char c] is a document that consists of the single character [c]. This - character must not be a newline. *) +(**[char c] is an atomic document that consists of the single character [c]. + This character must not be a newline character. *) val char: char -> document -(** [string s] is a document that consists of the string [s]. This string must - not contain a newline. *) +(**[string s] is an atomic document that consists of the string [s]. This + string must not contain a newline. The printing engine assumes that the + ideal width of this string is [String.length s]. This assumption is safe + if this is an ASCII string. Otherwise, {!fancystring} or {!utf8string} + should be preferred. *) val string: string -> document -(** [substring s ofs len] is a document that consists of the portion of the - string [s] delimited by the offset [ofs] and the length [len]. This - portion must not contain a newline. *) +(**[substring s ofs len] is an atomic document that consists of the portion + of the string [s] delimited by the offset [ofs] and the length [len]. + This portion must not contain a newline. [substring s ofs len] is + equivalent to [string (String.sub s ofs len)], but is expected to be more + efficient, as the substring is not actually extracted. *) val substring: string -> int -> int -> document -(** [fancystring s apparent_length] is a document that consists of the string - [s]. This string must not contain a newline. The string may contain fancy - characters: color escape characters, UTF-8 or multi-byte characters, - etc. Thus, its apparent length (which measures how many columns the text - will take up on screen) differs from its length in bytes. *) +(**[fancystring s alen] is an atomic document that consists of the string + [s]. This string must not contain a newline. The string may contain fancy + characters: color escape characters, UTF-8 characters, etc. Thus, its + apparent length (which measures how many columns the text will take up on + screen) differs from its length in bytes. The printing engine assumes + that its apparent length is [alen]. *) val fancystring: string -> int -> document -(** [fancysubstring s ofs len apparent_length] is a document that consists of - the portion of the string [s] delimited by the offset [ofs] and the length - [len]. This portion must contain a newline. The string may contain fancy - characters. *) +(**[fancysubstring s ofs len alen] is equivalent to [fancystring (String.sub + s ofs len) alen]. *) val fancysubstring : string -> int -> int -> int -> document -(** [utf8string s] is a document that consists of the UTF-8-encoded string [s]. - This string must not contain a newline. *) +(**[utf8string s] is an atomic document that consists of the UTF-8-encoded + string [s]. This string must not contain a newline. [utf8string s] is + equivalent to [fancystring s (utf8_length s)], where [utf8_length s] is + the apparent length of the UTF-8-encoded string [s]. *) val utf8string: string -> document -(** [hardline] is a forced newline document. This document forces all enclosing - groups to be printed in non-flattening mode. In other words, any enclosing - groups are dissolved. *) +(** [utf8format format ...] is equivalent to + [utf8string (Printf.sprintf format ...)]. *) +val utf8format: ('a, unit, string, document) format4 -> 'a + +(** {2 Blanks and Newlines} *) + +(**The atomic document [hardline] represents a forced newline. This document + has infinite ideal width: thus, if there is a choice between printing it + in flat mode and printing it in normal mode, normal mode is preferred. In + other words, when [hardline] is placed directly inside a group, this + group is dissolved: [group hardline] is equivalent to [hardline]. This + combinator should be seldom used; consider using {!break} instead. *) val hardline: document -(** [blank n] is a document that consists of [n] blank characters. *) +(**The atomic document [blank n] consists of [n] blank characters. A blank + character is like an ordinary ASCII space character [char ' '], except + that blank characters that appear at the end of a line are automatically + suppressed. *) val blank: int -> document -(** [break n] is a document which consists of either [n] blank characters, - when forced to display on a single line, or a single newline character, - otherwise. Note that there is no choice at this point: choices are encoded - by the [group] combinator. *) +(**[space] is a synonym for [blank 1]. It consists of one blank character. + It is therefore not equivalent to [char ' ']. *) +val space: document + +(**The document [break n] is a breakable blank of width [n]. It produces [n] + blank characters if the printing engine is in flat mode, and a single + newline character if the printing engine is in normal mode. [break 1] is + equivalent to [ifflat (blank 1) hardline]. *) val break: int -> document -(** [doc1 ^^ doc2] is the concatenation of the documents [doc1] and [doc2]. *) -val (^^): document -> document -> document +(** {2 Composite Documents} *) -(** [nest j doc] is the document [doc], in which the indentation level has - been increased by [j], that is, in which [j] blanks have been inserted - after every newline character. Read this again: indentation is inserted - after every newline character. No indentation is inserted at the beginning - of the document. *) -val nest: int -> document -> document +(**[doc1 ^^ doc2] is the concatenation of the documents [doc1] and [doc2]. *) +val (^^): document -> document -> document -(** [group doc] encodes a choice. If possible, then the entire document [group - doc] is rendered on a single line. Otherwise, the group is dissolved, and - [doc] is rendered. There might be further groups within [doc], whose - presence will lead to further choices being explored. *) +(**[group doc] encodes a choice. If the document [doc] fits on the current + line, then it is rendered on a single line, in flat mode. (All [group] + combinators inside it are then ignored.) Otherwise, this group is + dissolved, and [doc] is rendered in normal mode. There might be more + groups within [doc], whose presence leads to further choices being + explored. *) val group: document -> document -(** [ifflat doc1 doc2] is rendered as [doc1] if part of a group that can be - successfully flattened, and is rendered as [doc2] otherwise. Use this - operation with caution. Because the pretty-printer is free to choose - between [doc1] and [doc2], these documents should be semantically - equivalent. *) +(**[ifflat doc1 doc2] is rendered as [doc1] if the printing engine is in + flat mode, that is, if the printing engine has determined that some + enclosing group fits on the current line. Otherwise, it is rendered as + [doc2]. Use this combinator with caution! Because the printing engine is + free to choose between [doc1] and [doc2], these documents must be + semantically equivalent. It is up to the user to enforce this property. *) val ifflat: document -> document -> document -(** [align doc] is the document [doc], in which the indentation level has been - set to the current column. Thus, [doc] is rendered within a box whose - upper left corner is the current position. *) +(**To render the document [nest j doc], the printing engine temporarily + increases the current indentation level by [j], then renders [doc]. The + effect of the current indentation level is as follows: every time a + newline character is emitted, it is immediately followed by [n] blank + characters, where [n] is the current indentation level. Thus, one may + think of [nest j doc] roughly as the document [doc] in which [j] blank + characters have been inserted after every newline character. *) +val nest: int -> document -> document + +(**To render [align doc], the printing engine sets the current indentation + level to the current column, then renders [doc]. In other words, the + document [doc] is rendered within a box whose upper left corner is the + current position of the printing engine. *) val align: document -> document -(** {1 Rendering documents} *) +(**A point is a pair of a line number and a column number. *) +type point = + int * int + +(**A range is a pair of points. *) +type range = + point * point + +(**The document [range hook doc] is printed like the document [doc], but + allows the caller to register a hook that is applied, when the document + is printed, to the range occupied by this document in the output text. + This offers a way of mapping positions in the output text back to + (sub)documents. *) +val range: (range -> unit) -> document -> document + +(** {1:rendering Rendering Documents} *) + +(**Three renderers are available. They offer the same API, described + by the signature {!RENDERER}, and differ only in the nature of the + output channel that they use. *) + +(**This signature describes the document renderers in a manner that + is independent of the type of the output channel. *) +module type RENDERER = sig + + (**The type of the output channel. *) + type channel -(** This renderer sends its output into an output channel. *) -module ToChannel : PPrintRenderer.RENDERER + (**The type of documents. *) + type document + + (** [pretty rfrac width channel document] pretty-prints the document + [document] into the output channel [channel]. The parameter [width] is + the maximum number of characters per line. The parameter [rfrac] is the + ribbon width, a fraction relative to [width]. The ribbon width is the + maximum number of non-indentation characters per line. *) + val pretty: float -> int -> channel -> document -> unit + + (** [compact channel document] prints the document [document] to the output + channel [channel]. No indentation is used. All newline instructions are + respected, that is, no groups are flattened. *) + val compact: channel -> document -> unit + +end + +(**This renderer sends its output into an output channel. *) +module ToChannel : RENDERER with type channel = out_channel and type document = document -(** This renderer sends its output into a memory buffer. *) -module ToBuffer : PPrintRenderer.RENDERER +(**This renderer sends its output into a memory buffer. *) +module ToBuffer : RENDERER with type channel = Buffer.t and type document = document -(** This renderer sends its output into a formatter channel. *) -module ToFormatter : PPrintRenderer.RENDERER +(**This renderer sends its output into a formatter channel. *) +module ToFormatter : RENDERER with type channel = Format.formatter and type document = document -(** {1 Defining custom documents} *) +(** {1:defining Defining Custom Documents} *) -(** A width requirement is expressed as an integer, where the value [max_int] - is reserved and represents infinity. *) +(**It is possible to define custom document constructors, provided they meet + the expectations of the printing engine. In short, the custom document + combinator {!val-custom} expects an object of class {!class-type-custom}. + This object must provide three methods. The method [requirement] must + compute the ideal width of the custom document. The methods [pretty] and + [compact] must render the custom document. For this purpose, they have + access to the {{!output}output channel} and to the {{!state}state} of the + printing engine. *) +(** A width requirement is expressed as an integer. The value [max_int] + is reserved and represents infinity. *) type requirement = int -val infinity : requirement -(** An output channel is represented abstractly as an object equipped with - methods for displaying one character and for displaying a substring. *) +(**[infinity] represents an infinite width requirement. *) +val infinity : requirement +(**An output channel is abstractly represented as an object equipped with + methods for displaying one character and for displaying a substring. *) class type output = object - (** [char c] sends the character [c] to the output channel. *) + (**[char c] sends the character [c] to the output channel. *) method char: char -> unit - (** [substring s ofs len] sends the substring of [s] delimited by the - offset [ofs] and the length [len] to the output channel. *) + (**[substring s ofs len] sends the substring of [s] delimited by the + offset [ofs] and the length [len] to the output channel. *) method substring: string -> int (* offset *) -> int (* length *) -> unit end -(** The rendering engine maintains the following internal state. Its structure - is subject to change in future versions of the library. Nevertheless, it is - exposed to the user who wishes to define custom documents. *) - +(**The internal state of the rendering engine is exposed to the user who + wishes to define custom documents. However, its structure is subject to + change in future versions of the library. *) type state = { width: int; @@ -157,10 +233,14 @@ type state = { mutable last_indent: int; (** The number of blanks that were printed at the beginning of the current - line. This field is updated (only) by the function [emit_hardline]. It - is used (only) to determine whether the ribbon width constraint is + line. This field is updated (only) when a hardline is emitted. It is + used (only) to determine whether the ribbon width constraint is respected. *) + mutable line: int; + (** The current line. This field is updated (only) when a hardline is + emitted. It is not used by the pretty-printing engine itself. *) + mutable column: int; (** The current column. This field must be updated whenever something is sent to the output channel. It is used (only) to determine whether the @@ -168,59 +248,60 @@ type state = { } -(** A custom document is defined by implementing the following methods. *) - +(**A custom document is defined by implementing an object of class + {!class-type-custom}. *) class type custom = object - (** A custom document must publish the width (i.e., the number of columns) - that it would like to occupy if it is printed on a single line (that is, - in flattening mode). The special value [infinity] means that this - document cannot be printed on a single line; this value causes any - groups that contain this document to be dissolved. This method should - in principle work in constant time. *) + (**A custom document must publish the width (i.e., the number of columns) + that it would like to occupy if printed on a single line (in flat + mode). The special value [infinity] means that this document cannot be + printed on a single line; this value causes any groups that contain + this document to be dissolved. This method should in principle work in + constant time. *) method requirement: requirement - (** The method [pretty] is used by the main rendering algorithm. It has - access to the output channel and to the algorithm's internal state, as - described above. In addition, it receives the current indentation level - and the current flattening mode (on or off). If flattening mode is on, - then the document must be printed on a single line, in a manner that is - consistent with the requirement that was published ahead of time. If - flattening mode is off, then there is no such obligation. The state must - be updated in a manner that is consistent with what is sent to the - output channel. *) + (**The method [pretty] is used by the main rendering algorithm. It has + access to the output channel and to the printing engine's internal + state. In addition, it receives the current indentation level and a + Boolean flag that tells whether the engine is currently in flat mode. + If the engine is in flat mode, then the document must be printed on a + single line, in a manner that is consistent with the width requirement + that was published ahead of time. If the engine is in normal mode, then + there is no such obligation. The state must be updated in a manner that + is consistent with what is sent to the output channel. *) method pretty: output -> state -> int -> bool -> unit - (** The method [compact] is used by the compact rendering algorithm. It has - access to the output channel only. *) + (**The method [compact] is used by the compact rendering algorithm. It + has access to the output channel only. *) method compact: output -> unit end -(** The function [custom] constructs a custom document. In other words, it - converts an object of type [custom] to a document. *) +(**[custom] constructs a custom document out an object of type + {!class-type-custom}. *) val custom: custom -> document -(** The key functions of the library are exposed, in the hope that they may be - useful to authors of custom (leaf and non-leaf) documents. In the case of - a leaf document, they can help perform certain basic functions; for - instance, applying the function [pretty] to the document [hardline] is a - simple way of printing a hardline, while respecting the indentation - parameters and updating the state in a correct manner. Similarly, applying - [pretty] to the document [blank n] is a simple way of printing [n] spaces. - In the case of a non-leaf document (i.e., one which contains - sub-documents), these functions are essential: they allow computing the - width requirement of a sub-document and displaying a sub-document. *) - -(** [requirement doc] computes the width requirement of the document [doc]. - It works in constant time. *) +(**Some of the key functions of the library are exposed, in the hope that + they may be useful to authors of custom (leaf and composite) documents. + In the case of a leaf document, they can help perform certain basic + functions; for instance, applying the function {!pretty} to the document + {!hardline} is a simple way of printing a hardline, while respecting the + indentation parameters and updating the state in a correct manner. + Similarly, applying {!pretty} to the document [blank n] is a simple way + of printing [n] blank characters. In the case of a composite document + (one that contains subdocuments), these functions are essential: they + allow computing the width requirement of a subdocument and displaying a + subdocument. *) + +(**[requirement doc] computes the width requirement of the document [doc]. + It runs in constant time. *) val requirement: document -> requirement -(** [pretty output state indent flatten doc] prints the document [doc]. See - the documentation of the method [pretty]. *) +(**[pretty output state indent flatten doc] prints the document [doc]. See + the documentation of the method [pretty] in the class + {!class-type-custom}. *) val pretty: output -> state -> int -> bool -> document -> unit -(** [compact output doc] prints the document [doc]. See the documentation of - the method [compact]. *) +(**[compact output doc] prints the document [doc]. See the documentation of + the method [compact] in the class {!class-type-custom}. *) val compact: output -> document -> unit - diff --git a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintRenderer.ml b/tests/menhir_tests/test10menhir_with_aux_rules/PPrintRenderer.ml deleted file mode 100644 index 3449d6c..0000000 --- a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintRenderer.ml +++ /dev/null @@ -1,37 +0,0 @@ -(**************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2017 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(**************************************************************************) - -(** A common signature for the multiple document renderers proposed by {!PPrintEngine}. *) - -module type RENDERER = sig - - (** Output channels. *) - type channel - - (** Documents. *) - type document - - (** [pretty rfrac width channel document] pretty-prints the document - [document] into the output channel [channel]. The parameter [width] is - the maximum number of characters per line. The parameter [rfrac] is the - ribbon width, a fraction relative to [width]. The ribbon width is the - maximum number of non-indentation characters per line. *) - val pretty: float -> int -> channel -> document -> unit - - (** [compact channel document] prints the document [document] to the output - channel [channel]. No indentation is used. All newline instructions are - respected, that is, no groups are flattened. *) - val compact: channel -> document -> unit - -end - From 2c13c51b97c91890de863c05a4d575414a9b401d Mon Sep 17 00:00:00 2001 From: Kate Date: Mon, 3 Jan 2022 16:08:33 +0000 Subject: [PATCH 15/46] Enable automated testing of the OCaml code generation --- ott.opam | 5 +++++ tests/menhir_tests/test_if/Makefile | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ott.opam b/ott.opam index 909ba89..c0fc9d9 100644 --- a/ott.opam +++ b/ott.opam @@ -7,11 +7,16 @@ homepage: "http://www.cl.cam.ac.uk/~pes20/ott/" bug-reports: "https://github.com/ott-lang/ott/issues" depends: [ "ocaml" {>= "4.02.0"} + "pprint" {with-test & < "20220103"} + "menhir" {with-test} ] build: [ [make "world"] [make "ott.install"] ] +run-test: [ + [make "-C" "tests/menhir_tests/test_if"] +] dev-repo: "git+https://github.com/ott-lang/ott.git" synopsis: "A tool for writing definitions of programming languages and calculi" description: """ diff --git a/tests/menhir_tests/test_if/Makefile b/tests/menhir_tests/test_if/Makefile index 16b13b7..614a211 100644 --- a/tests/menhir_tests/test_if/Makefile +++ b/tests/menhir_tests/test_if/Makefile @@ -12,7 +12,7 @@ MENHIR := menhir MENHIRFLAGS := --infer --explain --unused-tokens --trace --base $(ROOT)_parser -OCAMLBUILD := ocamlbuild -use-ocamlfind -use-menhir -menhir "$(MENHIR) $(MENHIRFLAGS) ../$(MENHIR_EXTRA_LIB) " +OCAMLBUILD := ocamlbuild -use-ocamlfind -use-menhir -menhir "$(MENHIR) $(MENHIRFLAGS) ../$(MENHIR_EXTRA_LIB) " -package pprint MAIN := main From 154140dae72fc6d076e231bb146f314dbbd43913 Mon Sep 17 00:00:00 2001 From: Kartik Singhal Date: Wed, 19 Aug 2020 09:47:46 -0500 Subject: [PATCH 16/46] Add relative path to ott binary in 1Bsemantics example --- examples/1Bsemantics/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/1Bsemantics/Makefile b/examples/1Bsemantics/Makefile index 73701e2..c299352 100644 --- a/examples/1Bsemantics/Makefile +++ b/examples/1Bsemantics/Makefile @@ -1,5 +1,5 @@ -OTT=~/github/ott/bin/ott +OTT=../../bin/ott l1: l1.ott $(OTT) -pp_grammar -isa_syntax true l1.ott -o l1.tex -o l1.thy -o l1.lem From 6e3ecec60fe0a4974559d0a0ffa14e8b9f736267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20P=C3=A9rami?= Date: Thu, 3 Mar 2022 17:09:49 +0000 Subject: [PATCH 17/46] Allow recent PPrint version in opam file --- ott.opam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ott.opam b/ott.opam index c0fc9d9..4c48380 100644 --- a/ott.opam +++ b/ott.opam @@ -7,7 +7,7 @@ homepage: "http://www.cl.cam.ac.uk/~pes20/ott/" bug-reports: "https://github.com/ott-lang/ott/issues" depends: [ "ocaml" {>= "4.02.0"} - "pprint" {with-test & < "20220103"} + "pprint" {with-test} "menhir" {with-test} ] build: [ From 6140722834351e5c63590f0086e854241f655fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20P=C3=A9rami?= Date: Thu, 3 Mar 2022 17:26:33 +0000 Subject: [PATCH 18/46] Remove embedded PPrint and add corresponding opam tests --- ott.opam | 3 + tests/menhir_tests/test10menhir/Makefile | 4 +- tests/menhir_tests/test10menhir/PPrint.ml | 468 ----------- tests/menhir_tests/test10menhir/PPrint.mli | 345 -------- .../menhir_tests/test10menhir/PPrintEngine.ml | 751 ------------------ .../test10menhir/PPrintEngine.mli | 307 ------- .../test10menhir_with_aux_args/Makefile | 2 +- .../test10menhir_with_aux_args/PPrint.ml | 468 ----------- .../test10menhir_with_aux_args/PPrint.mli | 345 -------- .../PPrintEngine.ml | 751 ------------------ .../PPrintEngine.mli | 307 ------- .../pprint-master.zip | Bin 51643 -> 0 bytes .../test10menhir_with_aux_rules/Makefile | 2 +- .../test10menhir_with_aux_rules/PPrint.ml | 468 ----------- .../test10menhir_with_aux_rules/PPrint.mli | 345 -------- .../PPrintEngine.ml | 751 ------------------ .../PPrintEngine.mli | 307 ------- .../pprint-master.zip | Bin 51643 -> 0 bytes 18 files changed, 7 insertions(+), 5617 deletions(-) delete mode 100644 tests/menhir_tests/test10menhir/PPrint.ml delete mode 100644 tests/menhir_tests/test10menhir/PPrint.mli delete mode 100644 tests/menhir_tests/test10menhir/PPrintEngine.ml delete mode 100644 tests/menhir_tests/test10menhir/PPrintEngine.mli delete mode 100644 tests/menhir_tests/test10menhir_with_aux_args/PPrint.ml delete mode 100644 tests/menhir_tests/test10menhir_with_aux_args/PPrint.mli delete mode 100644 tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.ml delete mode 100644 tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.mli delete mode 100644 tests/menhir_tests/test10menhir_with_aux_args/pprint-master.zip delete mode 100644 tests/menhir_tests/test10menhir_with_aux_rules/PPrint.ml delete mode 100644 tests/menhir_tests/test10menhir_with_aux_rules/PPrint.mli delete mode 100644 tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.ml delete mode 100644 tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.mli delete mode 100644 tests/menhir_tests/test10menhir_with_aux_rules/pprint-master.zip diff --git a/ott.opam b/ott.opam index 4c48380..cb66f6e 100644 --- a/ott.opam +++ b/ott.opam @@ -16,6 +16,9 @@ build: [ ] run-test: [ [make "-C" "tests/menhir_tests/test_if"] + [make "-C" "tests/menhir_tests/test10menhir"] + [make "-C" "tests/menhir_tests/test10menhir_with_aux_args"] + [make "-C" "tests/menhir_tests/test10menhir_with_aux_rules"] ] dev-repo: "git+https://github.com/ott-lang/ott.git" synopsis: "A tool for writing definitions of programming languages and calculi" diff --git a/tests/menhir_tests/test10menhir/Makefile b/tests/menhir_tests/test10menhir/Makefile index 3144e3e..7b5468c 100644 --- a/tests/menhir_tests/test10menhir/Makefile +++ b/tests/menhir_tests/test10menhir/Makefile @@ -8,7 +8,7 @@ MENHIR := menhir MENHIRFLAGS := --infer -OCAMLBUILD := ocamlbuild -use-ocamlfind -use-menhir -menhir "$(MENHIR) $(MENHIRFLAGS)" +OCAMLBUILD := ocamlbuild -use-ocamlfind -use-menhir -menhir "$(MENHIR) $(MENHIRFLAGS)" -package pprint MAIN := main @@ -41,4 +41,4 @@ clean: realclean: $(MAKE) clean - rm -rf *.pdf \ No newline at end of file + rm -rf *.pdf diff --git a/tests/menhir_tests/test10menhir/PPrint.ml b/tests/menhir_tests/test10menhir/PPrint.ml deleted file mode 100644 index da55896..0000000 --- a/tests/menhir_tests/test10menhir/PPrint.ml +++ /dev/null @@ -1,468 +0,0 @@ -(******************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2022 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(******************************************************************************) - -include PPrintEngine - -(* -------------------------------------------------------------------------- *) - -(* Predefined single-character documents. *) - -let lparen = char '(' -let rparen = char ')' -let langle = char '<' -let rangle = char '>' -let lbrace = char '{' -let rbrace = char '}' -let lbracket = char '[' -let rbracket = char ']' -let squote = char '\'' -let dquote = char '"' -let bquote = char '`' -let semi = char ';' -let colon = char ':' -let comma = char ',' -let dot = char '.' -let sharp = char '#' -let slash = char '/' -let backslash = char '\\' -let equals = char '=' -let qmark = char '?' -let tilde = char '~' -let at = char '@' -let percent = char '%' -let dollar = char '$' -let caret = char '^' -let ampersand = char '&' -let star = char '*' -let plus = char '+' -let minus = char '-' -let underscore = char '_' -let bang = char '!' -let bar = char '|' - -(* -------------------------------------------------------------------------- *) - -(* Repetition. *) - -let twice doc = - doc ^^ doc - -let repeat n doc = - let rec loop n doc accu = - if n = 0 then - accu - else - loop (n - 1) doc (doc ^^ accu) - in - loop n doc empty - -(* -------------------------------------------------------------------------- *) - -(* Delimiters. *) - -let precede l x = l ^^ x -let terminate r x = x ^^ r -let enclose l r x = l ^^ x ^^ r - -let squotes = enclose squote squote -let dquotes = enclose dquote dquote -let bquotes = enclose bquote bquote -let braces = enclose lbrace rbrace -let parens = enclose lparen rparen -let angles = enclose langle rangle -let brackets = enclose lbracket rbracket - -(* -------------------------------------------------------------------------- *) - -(* Some functions on lists. *) - -(* A variant of [fold_left] that keeps track of the element index. *) - -let foldli (f : int -> 'b -> 'a -> 'b) (accu : 'b) (xs : 'a list) : 'b = - let r = ref 0 in - List.fold_left (fun accu x -> - let i = !r in - r := i + 1; - f i accu x - ) accu xs - -(* -------------------------------------------------------------------------- *) - -(* Working with lists of documents. *) - -let concat docs = - (* We take advantage of the fact that [^^] operates in constant - time, regardless of the size of its arguments. The document - that is constructed is essentially a reversed list (i.e., a - tree that is biased towards the left). This is not a problem; - when pretty-printing this document, the engine will descend - along the left branch, pushing the nodes onto its stack as - it goes down, effectively reversing the list again. *) - List.fold_left (^^) empty docs - -let separate sep docs = - foldli (fun i accu doc -> - if i = 0 then - doc - else - accu ^^ sep ^^ doc - ) empty docs - -let concat_map f xs = - List.fold_left (fun accu x -> - accu ^^ f x - ) empty xs - -let separate_map sep f xs = - foldli (fun i accu x -> - if i = 0 then - f x - else - accu ^^ sep ^^ f x - ) empty xs - -let separate2 sep last_sep docs = - let n = List.length docs in - foldli (fun i accu doc -> - if i = 0 then - doc - else - accu ^^ (if i < n - 1 then sep else last_sep) ^^ doc - ) empty docs - -let optional f = function - | None -> - empty - | Some x -> - f x - -(* -------------------------------------------------------------------------- *) - -(* Text. *) - -(* This variant of [String.index_from] returns an option. *) - -let index_from s i c = - try - Some (String.index_from s i c) - with Not_found -> - None - -(* [lines s] chops the string [s] into a list of lines, which are turned - into documents. *) - -let lines s = - let rec chop accu i = - match index_from s i '\n' with - | Some j -> - let accu = substring s i (j - i) :: accu in - chop accu (j + 1) - | None -> - substring s i (String.length s - i) :: accu - in - List.rev (chop [] 0) - -let arbitrary_string s = - separate (break 1) (lines s) - -(* [split ok s] splits the string [s] at every occurrence of a character - that satisfies the predicate [ok]. The substrings thus obtained are - turned into documents, and a list of documents is returned. No information - is lost: the concatenation of the documents yields the original string. - This code is not UTF-8 aware. *) - -let split ok s = - let n = String.length s in - let rec index_from i = - if i = n then - None - else if ok s.[i] then - Some i - else - index_from (i + 1) - in - let rec chop accu i = - match index_from i with - | Some j -> - let accu = substring s i (j - i) :: accu in - let accu = char s.[j] :: accu in - chop accu (j + 1) - | None -> - substring s i (String.length s - i) :: accu - in - List.rev (chop [] 0) - -(* [words s] chops the string [s] into a list of words, which are turned - into documents. *) - -let words s = - let n = String.length s in - (* A two-state finite automaton. *) - (* In this state, we have skipped at least one blank character. *) - let rec skipping accu i = - if i = n then - (* There was whitespace at the end. Drop it. *) - accu - else match s.[i] with - | ' ' - | '\t' - | '\n' - | '\r' -> - (* Skip more whitespace. *) - skipping accu (i + 1) - | _ -> - (* Begin a new word. *) - word accu i (i + 1) - (* In this state, we have skipped at least one non-blank character. *) - and word accu i j = - if j = n then - (* Final word. *) - substring s i (j - i) :: accu - else match s.[j] with - | ' ' - | '\t' - | '\n' - | '\r' -> - (* A new word has been identified. *) - let accu = substring s i (j - i) :: accu in - skipping accu (j + 1) - | _ -> - (* Continue inside the current word. *) - word accu i (j + 1) - in - List.rev (skipping [] 0) - -let flow_map sep f docs = - foldli (fun i accu doc -> - if i = 0 then - f doc - else - accu ^^ - (* This idiom allows beginning a new line if [doc] does not - fit on the current line. *) - group (sep ^^ f doc) - ) empty docs - -let flow sep docs = - flow_map sep (fun x -> x) docs - -let url s = - flow (break 0) (split (function '/' | '.' -> true | _ -> false) s) - -(* -------------------------------------------------------------------------- *) -(* Alignment and indentation. *) - -let hang i d = - align (nest i d) - -let ( !^ ) = string - -let ( ^/^ ) x y = - x ^^ break 1 ^^ y - -let prefix n b x y = - group (x ^^ nest n (break b ^^ y)) - -let (^//^) = - prefix 2 1 - -let jump n b y = - group (nest n (break b ^^ y)) - -let infix n b op x y = - prefix n b (x ^^ blank b ^^ op) y - -let surround n b opening contents closing = - group (opening ^^ nest n ( break b ^^ contents) ^^ break b ^^ closing ) - -let soft_surround n b opening contents closing = - group (opening ^^ nest n (group (break b) ^^ contents) ^^ group (break b ^^ closing)) - -let surround_separate n b void opening sep closing docs = - match docs with - | [] -> - void - | _ :: _ -> - surround n b opening (separate sep docs) closing - -let surround_separate_map n b void opening sep closing f xs = - match xs with - | [] -> - void - | _ :: _ -> - surround n b opening (separate_map sep f xs) closing - -(* -------------------------------------------------------------------------- *) -(* Printing OCaml values. *) - -module OCaml = struct - -open Printf - -type constructor = string -type type_name = string -type record_field = string -type tag = int - -(* -------------------------------------------------------------------------- *) - -(* This internal [sprintf]-like function produces a document. We use [string], - as opposed to [arbitrary_string], because the strings that we produce will - never contain a newline character. *) - -let dsprintf format = - ksprintf string format - -(* -------------------------------------------------------------------------- *) - -(* Nicolas prefers using this code as opposed to just [sprintf "%g"] or - [sprintf "%f"]. The latter print [inf] and [-inf], whereas OCaml - understands [infinity] and [neg_infinity]. [sprintf "%g"] does not add a - trailing dot when the number happens to be an integral number. [sprintf - "%F"] seems to lose precision and ignores the precision modifier. *) - -let valid_float_lexeme (s : string) : string = - let l = String.length s in - let rec loop i = - if i >= l then - (* If we reach the end of the string and have found only characters in - the set '0' .. '9' and '-', then this string will be considered as an - integer literal by OCaml. Adding a trailing dot makes it a float - literal. *) - s ^ "." - else - match s.[i] with - | '0' .. '9' | '-' -> loop (i + 1) - | _ -> s - in loop 0 - -(* This function constructs a string representation of a floating point - number. This representation is supposed to be accepted by OCaml as a - valid floating point literal. *) - -let float_representation (f : float) : string = - match classify_float f with - | FP_nan -> - "nan" - | FP_infinite -> - if f < 0.0 then "neg_infinity" else "infinity" - | _ -> - (* Try increasing precisions and validate. *) - let s = sprintf "%.12g" f in - if f = float_of_string s then valid_float_lexeme s else - let s = sprintf "%.15g" f in - if f = float_of_string s then valid_float_lexeme s else - sprintf "%.18g" f - -(* -------------------------------------------------------------------------- *) - -(* A few constants and combinators, used below. *) - -let some = - string "Some" - -let none = - string "None" - -let lbracketbar = - string "[|" - -let rbracketbar = - string "|]" - -let seq1 opening separator closing = - surround_separate 2 0 - (opening ^^ closing) opening (separator ^^ break 1) closing - -let seq2 opening separator closing = - surround_separate_map 2 1 - (opening ^^ closing) opening (separator ^^ break 1) closing - -(* -------------------------------------------------------------------------- *) - -(* The following functions are printers for many types of OCaml values. *) - -(* There is no protection against cyclic values. *) - -let tuple = - seq1 lparen comma rparen - -let variant _ cons _ args = - match args with - | [] -> - !^cons - | _ :: _ -> - !^cons ^^ tuple args - -let record _ fields = - seq2 lbrace semi rbrace (fun (k, v) -> infix 2 1 equals !^k v) fields - -let option f = function - | None -> - none - | Some x -> - some ^^ tuple [f x] - -let list f xs = - seq2 lbracket semi rbracket f xs - -let flowing_list f xs = - group (lbracket ^^ space ^^ nest 2 ( - flow_map (semi ^^ break 1) f xs - ) ^^ space ^^ rbracket) - -let array f xs = - seq2 lbracketbar semi rbracketbar f (Array.to_list xs) - -let flowing_array f xs = - group (lbracketbar ^^ space ^^ nest 2 ( - flow_map (semi ^^ break 1) f (Array.to_list xs) - ) ^^ space ^^ rbracketbar) - -let ref f x = - record "ref" ["contents", f !x] - -let float f = - string (float_representation f) - -let int = - dsprintf "%d" - -let int32 = - dsprintf "%ld" - -let int64 = - dsprintf "%Ld" - -let nativeint = - dsprintf "%nd" - -let char = - dsprintf "%C" - -let bool = - dsprintf "%B" - -let unit = - dsprintf "()" - -let string = - dsprintf "%S" - -let unknown tyname _ = - dsprintf "" tyname - -type representation = - document - -end (* OCaml *) diff --git a/tests/menhir_tests/test10menhir/PPrint.mli b/tests/menhir_tests/test10menhir/PPrint.mli deleted file mode 100644 index 89eeeeb..0000000 --- a/tests/menhir_tests/test10menhir/PPrint.mli +++ /dev/null @@ -1,345 +0,0 @@ -(******************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2022 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(******************************************************************************) - -include module type of PPrintEngine (** @inline *) - -(** {1:combinators High-Level Combinators} *) - -(** {2 Single Characters} *) - -(**The following atomic documents consist of a single character. Each of - them is a synonym for the application of {!char} to some constant - character. For instance, {!lparen} is a synonym for [char '(']. *) - -val lparen: document -val rparen: document -val langle: document -val rangle: document -val lbrace: document -val rbrace: document -val lbracket: document -val rbracket: document -val squote: document -val dquote: document -val bquote: document -val semi: document -val colon: document -val comma: document -val dot: document -val sharp: document -val slash: document -val backslash: document -val equals: document -val qmark: document -val tilde: document -val at: document -val percent: document -val dollar: document -val caret: document -val ampersand: document -val star: document -val plus: document -val minus: document -val underscore: document -val bang: document -val bar: document - -(** {2 Delimiters} *) - -(**[precede l x] is [l ^^ x]. *) -val precede: document -> document -> document - -(**[terminate r x] is [x ^^ r]. *) -val terminate: document -> document -> document - -(**[enclose l r x] is [l ^^ x ^^ r]. *) -val enclose: document -> document -> document -> document - -(**The following combinators enclose a document within a pair of delimiters. - They are partial applications of [enclose]. No whitespace or line break - is introduced. *) - -val squotes: document -> document -val dquotes: document -> document -val bquotes: document -> document -val braces: document -> document -val parens: document -> document -val angles: document -> document -val brackets: document -> document - -(** {2 Repetition} *) - -(**[twice doc] is the document obtained by concatenating two copies of - the document [doc]. *) -val twice: document -> document - -(**[repeat n doc] is the document obtained by concatenating [n] copies of - the document [doc]. *) -val repeat: int -> document -> document - -(** {2 Lists and Options} *) - -(**[concat docs] is the concatenation of the documents in the list [docs]. *) -val concat: document list -> document - -(**[separate sep docs] is the concatenation of the documents in the list - [docs]. The separator [sep] is inserted between every two adjacent - documents. *) -val separate: document -> document list -> document - -(**[concat_map f xs] is equivalent to [concat (List.map f xs)]. *) -val concat_map: ('a -> document) -> 'a list -> document - -(**[separate_map sep f xs] is equivalent to [separate sep (List.map f xs)]. *) -val separate_map: document -> ('a -> document) -> 'a list -> document - -(**[separate2 sep last_sep docs] is the concatenation of the documents in - the list [docs]. The separator [sep] is inserted between every two - adjacent documents, except between the last two documents, where the - separator [last_sep] is used instead. *) -val separate2: document -> document -> document list -> document - -(**[optional f None] is the empty document. [optional f (Some x)] is - the document [f x]. *) -val optional: ('a -> document) -> 'a option -> document - -(** {2 Text} *) - -(**[lines s] is the list of documents obtained by splitting [s] at newline - characters, and turning each line into a document via [substring]. This - code is not UTF-8 aware. *) -val lines: string -> document list - -(**[arbitrary_string s] is equivalent to [separate (break 1) (lines s)]. - It is analogous to [string s], but is valid even if the string [s] - contains newline characters. *) -val arbitrary_string: string -> document - -(**[words s] is the list of documents obtained by splitting [s] at whitespace - characters, and turning each word into a document via [substring]. All - whitespace is discarded. This code is not UTF-8 aware. *) -val words: string -> document list - -(**[split ok s] splits the string [s] before and after every occurrence of a - character that satisfies the predicate [ok]. The substrings thus obtained - are turned into documents, and a list of documents is returned. No - information is lost: the concatenation of the documents yields the - original string. This code is not UTF-8 aware. *) -val split: (char -> bool) -> string -> document list - -(**[flow sep docs] separates the documents in the list [docs] with the - separator [sep] and arranges for a new line to begin whenever a document - does not fit on the current line. This is useful for typesetting - free-flowing, ragged-right text. A typical choice of [sep] is [break b], - where [b] is the number of spaces that must be inserted between two - consecutive words (when displayed on the same line). *) -val flow: document -> document list -> document - -(**[flow_map sep f docs] is equivalent to [flow sep (List.map f docs)]. *) -val flow_map: document -> ('a -> document) -> 'a list -> document - -(**[url s] is a possible way of displaying the URL [s]. A potential line - break is inserted immediately before and immediately after every slash - and dot character. *) -val url: string -> document - -(** {2 Alignment and Indentation} *) - -(**[hang n doc] is analogous to [align], but additionally indents all lines, - except the first one, by [n]. Thus, the text in the box forms a hanging - indent. *) -val hang: int -> document -> document - -(**[prefix n b left right] has the following flat layout: - {[ - left right - ]} - and the following non-flat layout: - {[ - left - right - ]} - The parameter [n] controls the nesting of [right] (when not flat). - The parameter [b] controls the number of spaces between [left] and [right] - (when flat). *) -val prefix: int -> int -> document -> document -> document - -(**[jump n b right] is equivalent to [prefix n b empty right]. *) -val jump: int -> int -> document -> document - -(**[infix n b middle left right] has the following flat layout: - {[ - left middle right - ]} - and the following non-flat layout: - {[ - left middle - right - ]} - The parameter [n] controls the nesting of [right] (when not flat). - The parameter [b] controls the number of spaces between [left] and [middle] - (always) and between [middle] and [right] (when flat). *) -val infix: int -> int -> document -> document -> document -> document - -(**[surround n b opening contents closing] has the following flat layout: - {[ - opening contents closing - ]} - and the following non-flat layout: - {[ - opening - contents - closing - ]} - The parameter [n] controls the nesting of [contents] (when not flat). - The parameter [b] controls the number of spaces between [opening] and - [contents] and between [contents] and [closing] (when flat). -*) -val surround: int -> int -> document -> document -> document -> document - -(**[soft_surround] is analogous to [surround], but involves more than one - group, so it offers possibilities other than the completely flat layout - (where [opening], [contents], and [closing] appear on a single line) and - the completely developed layout (where [opening], [contents], and - [closing] appear on separate lines). It tries to place the beginning of - [contents] on the same line as [opening], and to place [closing] on the - same line as the end of [contents], if possible. *) -val soft_surround: int -> int -> document -> document -> document -> document - -(**[surround_separate n b void opening sep closing docs] is equivalent to - [surround n b opening (separate sep docs) closing], except when the list - [docs] is empty, in which case it reduces to [void]. *) -val surround_separate: - int -> int -> - document -> document -> document -> document -> - document list -> document - -(**[surround_separate_map n b void opening sep closing f xs] is equivalent - to [surround_separate n b void opening sep closing (List.map f xs)]. *) -val surround_separate_map: - int -> int -> - document -> document -> document -> document -> - ('a -> document) -> 'a list -> document - -(** {2 Short-Hands} *) - -(**[!^s] is a short-hand for [string s]. *) -val ( !^ ) : string -> document - -(**[x ^/^ y] separates [x] and [y] with a breakable space. - It is a short-hand for [x ^^ break 1 ^^ y]. *) -val ( ^/^ ) : document -> document -> document - -(**[x ^//^ y] is a short-hand for [prefix 2 1 x y]. *) -val ( ^//^ ) : document -> document -> document - -(** {1:ocaml Printing OCaml Values} *) - -(**This module offers document combinators that help print OCaml values. The - strings produced by rendering these documents are supposed to be accepted - by the OCaml parser as valid values. - - These functions do {i not} distinguish between mutable and immutable - values. They do {i not} recognize sharing, and do {i not} incorporate a - protection against cyclic values. *) -module OCaml : sig - -(* The signature of this module is compatible with that expected by the - [camlp4] generator [Camlp4RepresentationGenerator]. This explains why - some functions have unused parameters. This is also the reason why - there is a type [representation]. *) - -type constructor = string -type type_name = string -type record_field = string -type tag = int - -(**[variant _ dc _ args] represents a constructed value whose data - constructor is [dc] and whose arguments are [args]. The other two - parameters are presently unused. *) -val variant : type_name -> constructor -> tag -> document list -> document - -(**[record _ fields] represents a record value whose fields are [fields]. - The other parameter is presently unused. *) -val record : type_name -> (record_field * document) list -> document - -(**[tuple args] represents a tuple value whose components are [args]. *) -val tuple : document list -> document - -(**[string s] represents the literal string [s]. *) -val string : string -> document - -(**[int i] represents the literal integer [i]. *) -val int : int -> document - -(**[int32 i] represents the literal 32-bit integer [i]. *) -val int32 : int32 -> document - -(**[int64 i] represents the literal 64-bit integer [i]. *) -val int64 : int64 -> document - -(**[nativeint i] represents the literal native integer [i]. *) -val nativeint : nativeint -> document - -(**[float f] represents the literal floating-point number [f]. *) -val float : float -> document - -(**[char c] represents the literal character [c]. *) -val char : char -> document - -(**[bool b] represents the Boolean value [b]. *) -val bool : bool -> document - -(**[unit] represents the unit constant [()]. *) -val unit : document - -(**[option f o] represents the option [o]. The representation of the - element, if present, is computed by the function [f]. *) -val option : ('a -> document) -> 'a option -> document - -(**[list f xs] represents the list [xs]. The representation of each element - is computed by the function [f]. If the whole list fits on a single line, - then it is printed on a single line; otherwise each element is printed on - a separate line. *) -val list : ('a -> document) -> 'a list -> document - -(**[flowing_list f xs] represents the list [xs]. The representation of each - element is computed by the function [f]. As many elements are possible - are printed on each line. *) -val flowing_list : ('a -> document) -> 'a list -> document - -(**[array f xs] represents the array [xs]. The representation of each - element is computed by the function [f]. If the whole array fits on a - single line, then it is printed on a single line; otherwise each element - is printed on a separate line. *) -val array : ('a -> document) -> 'a array -> document - -(**[flowing_array f xs] represents the array [xs]. The representation of - each element is computed by the function [f]. As many elements are - possible are printed on each line. *) -val flowing_array : ('a -> document) -> 'a array -> document - -(**[ref r] represents the reference [r]. The representation of the content - is computed by the function [f]. *) -val ref : ('a -> document) -> 'a ref -> document - -(** [unknown t _] represents an unknown value of type [t]. It is rendered - as a string of the form []. *) -val unknown : type_name -> 'a -> document - -(**/**) - -type representation = - document - -end (* OCaml *) diff --git a/tests/menhir_tests/test10menhir/PPrintEngine.ml b/tests/menhir_tests/test10menhir/PPrintEngine.ml deleted file mode 100644 index 3131893..0000000 --- a/tests/menhir_tests/test10menhir/PPrintEngine.ml +++ /dev/null @@ -1,751 +0,0 @@ -(******************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2022 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(******************************************************************************) - -(** A point is a pair of a line number and a column number. *) -type point = - int * int - -(** A range is a pair of points. *) -type range = - point * point - -(* ------------------------------------------------------------------------- *) - -(* A type of integers with infinity. *) - -type requirement = - int (* with infinity *) - -(* Infinity is encoded as [max_int]. *) - -let infinity : requirement = - max_int - -(* Addition of integers with infinity. *) - -let (++) (x : requirement) (y : requirement) : requirement = - if x = infinity || y = infinity then - infinity - else - x + y - -(* Comparison between an integer with infinity and a normal integer. *) - -let (<==) (x : requirement) (y : int) = - x <= y - -(* ------------------------------------------------------------------------- *) - -(* A uniform interface for output channels. *) - -class type output = object - - (** [char c] sends the character [c] to the output channel. *) - method char: char -> unit - - (** [substring s ofs len] sends the substring of [s] delimited by the - offset [ofs] and the length [len] to the output channel. *) - method substring: string -> int (* offset *) -> int (* length *) -> unit - -end - -(* ------------------------------------------------------------------------- *) - -(* Printing blank space. This is used both internally (to emit indentation - characters) and via the public combinator [blank]. *) - -let blank_length = - 80 - -let blank_buffer = - String.make blank_length ' ' - -let rec blanks (output : output) n = - if n <= 0 then - () - else if n <= blank_length then - output#substring blank_buffer 0 n - else begin - output#substring blank_buffer 0 blank_length; - blanks output (n - blank_length) - end - -(* ------------------------------------------------------------------------- *) - -(* The class [buffering] implements a wrapper that delays the printing of - blank characters. This includes indentation characters and characters - produced by the combinator [blank]. The printing of these characters is - delayed until it is known that they are followed by something on the same - line; if they are not followed with anything, then it is canceled. - - The actual printing task is delegated to the object [delegate], whose type - is [output]; the new object has type [output] as well. *) - -class buffering (delegate : output) : output = object (self) - - (* The number of blank characters that are withholding. *) - val mutable buffered = 0 - - (* [flush] sends out the blank characters that have been withheld. *) - method private flush = - blanks delegate buffered; - buffered <- 0 - - method char c : unit = - begin match c with - | '\n' -> - (* The current line ends here. Any blank characters that were withheld - are destroyed. This is where we avoid printing blank characters if - nothing follows them. *) - buffered <- 0 - | _ -> - (* The current line is nonempty. Any blank characters that were - withheld can now be flushed. *) - self#flush - end; - (* Print this character as usual. *) - delegate#char c - - method substring s pos len = - (* If this is a string of length zero, then there is nothing to do. *) - if len = 0 then - () - (* If this is a blank string (which we recognize by its address), then - its content is withheld. *) - else if s == blank_buffer then - buffered <- buffered + len - (* If this is not a blank string, then the blank characters that were - withheld up to this point can now be flushed. *) - else begin - self#flush; - delegate#substring s pos len - end - -end - -(* ------------------------------------------------------------------------- *) - -(* Three kinds of output channels are wrapped so as to satisfy the above - interface: OCaml output channels, OCaml memory buffers, and OCaml - formatters. *) - -class channel_output channel = object - method char = output_char channel - method substring = output_substring channel - (* We used to use [output], but, as of OCaml 4.02 and with -safe-string - enabled, the type of [output] has changed: this function now expects - an argument of type [bytes]. The new function [output_substring] must - be used instead. Furthermore, as of OCaml 4.06, -safe-string is enabled - by default. In summary, we require OCaml 4.02, use [output_substring], - and enable -safe-string. *) -end - -class buffer_output buffer = object - method char = Buffer.add_char buffer - method substring = Buffer.add_substring buffer -end - -class formatter_output fmt = object - method char = function - | '\n' -> Format.pp_force_newline fmt () - | ' ' -> Format.pp_print_space fmt () - | c -> Format.pp_print_char fmt c - - method substring str ofs len = - Format.pp_print_text fmt ( - if ofs = 0 && len = String.length str - then str - else String.sub str ofs len - ) -end - -(* ------------------------------------------------------------------------- *) - -(** The rendering engine maintains the following internal state. Its structure - is subject to change in future versions of the library. Nevertheless, it is - exposed to the user who wishes to define custom documents. *) - -type state = { - - width: int; - (** The line width. This parameter is fixed throughout the execution of - the renderer. *) - - ribbon: int; - (** The ribbon width. This parameter is fixed throughout the execution of - the renderer. *) - - mutable last_indent: int; - (** The number of blanks that were printed at the beginning of the current - line. This field is updated (only) when a hardline is emitted. It is - used (only) to determine whether the ribbon width constraint is - respected. *) - - mutable line: int; - (** The current line. This field is updated (only) when a hardline is - emitted. It is not used by the pretty-printing engine itself. *) - - mutable column: int; - (** The current column. This field must be updated whenever something is - sent to the output channel. It is used (only) to determine whether the - width constraint is respected. *) - - } - -(* ------------------------------------------------------------------------- *) - -(* [initial rfrac width] creates a fresh initial state. *) - -let initial rfrac width = { - width = width; - ribbon = max 0 (min width (truncate (float_of_int width *. rfrac))); - last_indent = 0; - line = 0; - column = 0 -} - -(* ------------------------------------------------------------------------- *) - -(** A custom document is defined by implementing the following methods. *) - -class type custom = object - - (** A custom document must publish the width (i.e., the number of columns) - that it would like to occupy if it is printed on a single line (that is, - in flattening mode). The special value [infinity] means that this - document cannot be printed on a single line; this value causes any - groups that contain this document to be dissolved. This method should - in principle work in constant time. *) - method requirement: requirement - - (** The method [pretty] is used by the main rendering algorithm. It has - access to the output channel and to the algorithm's internal state, as - described above. In addition, it receives the current indentation level - and the current flattening mode (on or off). If flattening mode is on, - then the document must be printed on a single line, in a manner that is - consistent with the requirement that was published ahead of time. If - flattening mode is off, then there is no such obligation. The state must - be updated in a manner that is consistent with what is sent to the - output channel. *) - method pretty: output -> state -> int -> bool -> unit - - (** The method [compact] is used by the compact rendering algorithm. It has - access to the output channel only. *) - method compact: output -> unit - -end - -(* ------------------------------------------------------------------------- *) - -(* Here is the algebraic data type of documents. It is analogous to Daan - Leijen's version, but the binary constructor [Union] is replaced with - the unary constructor [Group], and the constant [Line] is replaced with - more general constructions, namely [IfFlat], which provides alternative - forms depending on the current flattening mode, and [HardLine], which - represents a newline character, and causes a failure in flattening mode. *) - -type document = - - (* [Empty] is the empty document. *) - - | Empty - - (* [Char c] is a document that consists of the single character [c]. We - enforce the invariant that [c] is not a newline character. *) - - | Char of char - - (* [String s] is a document that consists of just the string [s]. We - assume, but do not check, that this string does not contain a newline - character. [String] is a special case of [FancyString], which takes up - less space in memory. *) - - | String of string - - (* [FancyString (s, ofs, len, apparent_length)] is a (portion of a) string - that may contain fancy characters: color escape characters, UTF-8 or - multi-byte characters, etc. Thus, the apparent length (which corresponds - to what will be visible on screen) differs from the length (which is a - number of bytes, and is reported by [String.length]). We assume, but do - not check, that fancystrings do not contain a newline character. *) - - | FancyString of string * int * int * int - - (* [Blank n] is a document that consists of [n] blank characters. *) - - | Blank of int - - (* When in flattening mode, [IfFlat (d1, d2)] turns into the document - [d1]. When not in flattening mode, it turns into the document [d2]. *) - - | IfFlat of document * document - - (* When in flattening mode, [HardLine] causes a failure, which requires - backtracking all the way until the stack is empty. When not in flattening - mode, it represents a newline character, followed with an appropriate - number of indentation. A common way of using [HardLine] is to only use it - directly within the right branch of an [IfFlat] construct. *) - - | HardLine - - (* The following constructors store their space requirement. This is the - document's apparent length, if printed in flattening mode. This - information is computed in a bottom-up manner when the document is - constructed. *) - - (* In other words, the space requirement is the number of columns that the - document needs in order to fit on a single line. We express this value in - the set of `integers extended with infinity', and use the value - [infinity] to indicate that the document cannot be printed on a single - line. *) - - (* Storing this information at [Group] nodes is crucial, as it allows us to - avoid backtracking and buffering. *) - - (* Storing this information at other nodes allows the function [requirement] - to operate in constant time. This means that the bottom-up computation of - requirements takes linear time. *) - - (* [Cat (req, doc1, doc2)] is the concatenation of the documents [doc1] and - [doc2]. The space requirement [req] is the sum of the requirements of - [doc1] and [doc2]. *) - - | Cat of requirement * document * document - - (* [Nest (req, j, doc)] is the document [doc], in which the indentation - level has been increased by [j], that is, in which [j] blanks have been - inserted after every newline character. The space requirement [req] is - the same as the requirement of [doc]. *) - - | Nest of requirement * int * document - - (* [Group (req, doc)] represents an alternative: it is either a flattened - form of [doc], in which occurrences of [Group] disappear and occurrences - of [IfFlat] resolve to their left branch, or [doc] itself. The space - requirement [req] is the same as the requirement of [doc]. *) - - | Group of requirement * document - - (* [Align (req, doc)] increases the indentation level to reach the current - column. Thus, the document [doc] is rendered within a box whose upper - left corner is the current position. The space requirement [req] is the - same as the requirement of [doc]. *) - - | Align of requirement * document - - (* [Range (req, hook, doc)] is printed like [doc]. After it is printed, the - function [hook] is applied to the range that is occupied by [doc] in the - output. *) - - | Range of requirement * (range -> unit) * document - - (* [Custom (req, f)] is a document whose appearance is user-defined. *) - - | Custom of custom - -(* ------------------------------------------------------------------------- *) - -(* Retrieving or computing the space requirement of a document. *) - -let rec requirement = function - | Empty -> - 0 - | Char _ -> - 1 - | String s -> - String.length s - | FancyString (_, _, _, len) - | Blank len -> - len - | IfFlat (doc1, _) -> - (* In flattening mode, the requirement of [ifflat x y] is just the - requirement of its flat version, [x]. *) - (* The smart constructor [ifflat] ensures that [IfFlat] is never nested - in the left-hand side of [IfFlat], so this recursive call is not a - problem; the function [requirement] has constant time complexity. *) - requirement doc1 - | HardLine -> - (* A hard line cannot be printed in flattening mode. *) - infinity - | Cat (req, _, _) - | Nest (req, _, _) - | Group (req, _) - | Align (req, _) - | Range (req, _, _) -> - (* These nodes store their requirement -- which is computed when the - node is constructed -- so as to allow us to answer in constant time - here. *) - req - | Custom c -> - c#requirement - -(* ------------------------------------------------------------------------- *) - -(* The above algebraic data type is not exposed to the user. Instead, we - expose the following functions. These functions construct a raw document - and compute its requirement, so as to obtain a document. *) - -let empty = - Empty - -let char c = - assert (c <> '\n'); - Char c - -let space = - Blank 1 - -let string s = - String s - -let fancysubstring s ofs len apparent_length = - if len = 0 then - empty - else - FancyString (s, ofs, len, apparent_length) - -let substring s ofs len = - fancysubstring s ofs len len - -let fancystring s apparent_length = - fancysubstring s 0 (String.length s) apparent_length - -(* The following function was stolen from [Batteries]. *) -let utf8_length s = - let rec length_aux s c i = - if i >= String.length s then c else - let n = Char.code (String.unsafe_get s i) in - let k = - if n < 0x80 then 1 else - if n < 0xe0 then 2 else - if n < 0xf0 then 3 else 4 - in - length_aux s (c + 1) (i + k) - in - length_aux s 0 0 - -let utf8string s = - fancystring s (utf8_length s) - -let utf8format f = - Printf.ksprintf utf8string f - -let hardline = - HardLine - -let blank n = - match n with - | 0 -> - empty - | _ -> - Blank n - -let ifflat doc1 doc2 = - (* Avoid nesting [IfFlat] in the left-hand side of [IfFlat], as this - is redundant. *) - match doc1 with - | IfFlat (doc1, _) - | doc1 -> - IfFlat (doc1, doc2) - -let internal_break i = - ifflat (blank i) hardline - -let break0 = - internal_break 0 - -let break1 = - internal_break 1 - -let break i = - match i with - | 0 -> - break0 - | 1 -> - break1 - | _ -> - internal_break i - -let (^^) x y = - match x, y with - | Empty, _ -> - y - | _, Empty -> - x - | _, _ -> - Cat (requirement x ++ requirement y, x, y) - -let nest i x = - assert (i >= 0); - Nest (requirement x, i, x) - -let group x = - let req = requirement x in - (* Minor optimisation: an infinite requirement dissolves a group. *) - if req = infinity then - x - else - Group (req, x) - -let align x = - Align (requirement x, x) - -let range hook x = - Range (requirement x, hook, x) - -let custom c = - (* Sanity check. *) - assert (c#requirement >= 0); - Custom c - -(* ------------------------------------------------------------------------- *) - -(* This function expresses the following invariant: if we are in flattening - mode, then we must be within bounds, i.e. the width and ribbon width - constraints must be respected. *) - -let ok state flatten : bool = - not flatten || - state.column <= state.width && state.column <= state.last_indent + state.ribbon - -(* ------------------------------------------------------------------------- *) - -(* The pretty rendering engine. *) - -(* The renderer is supposed to behave exactly like Daan Leijen's, although its - implementation is quite radically different, and simpler. Our documents are - constructed eagerly, as opposed to lazily. This means that we pay a large - space overhead, but in return, we get the ability of computing information - bottom-up, as described above, which allows to render documents without - backtracking or buffering. *) - -(* The [state] record is never copied; it is just threaded through. In - addition to it, the parameters [indent] and [flatten] influence the - manner in which the document is rendered. *) - -(* The code is written in tail-recursive style, so as to avoid running out of - stack space if the document is very deep. Each [KCons] cell in a - continuation represents a pending call to [pretty]. Each [KRange] cell - represents a pending call to a user-provided range hook. *) - -type cont = - | KNil - | KCons of int * bool * document * cont - | KRange of (range -> unit) * point * cont - -let rec pretty - (output : output) - (state : state) - (indent : int) - (flatten : bool) - (doc : document) - (cont : cont) -: unit = - match doc with - - | Empty -> - continue output state cont - - | Char c -> - output#char c; - state.column <- state.column + 1; - (* assert (ok state flatten); *) - continue output state cont - - | String s -> - let len = String.length s in - output#substring s 0 len; - state.column <- state.column + len; - (* assert (ok state flatten); *) - continue output state cont - - | FancyString (s, ofs, len, apparent_length) -> - output#substring s ofs len; - state.column <- state.column + apparent_length; - (* assert (ok state flatten); *) - continue output state cont - - | Blank n -> - blanks output n; - state.column <- state.column + n; - (* assert (ok state flatten); *) - continue output state cont - - | HardLine -> - (* We cannot be in flattening mode, because a hard line has an [infinity] - requirement, and we attempt to render a group in flattening mode only - if this group's requirement is met. *) - assert (not flatten); - (* Emit a hardline. *) - output#char '\n'; - blanks output indent; - state.line <- state.line + 1; - state.column <- indent; - state.last_indent <- indent; - (* Continue. *) - continue output state cont - - | IfFlat (doc1, doc2) -> - (* Pick an appropriate sub-document, based on the current flattening - mode. *) - pretty output state indent flatten (if flatten then doc1 else doc2) cont - - | Cat (_, doc1, doc2) -> - (* Push the second document onto the continuation. *) - pretty output state indent flatten doc1 (KCons (indent, flatten, doc2, cont)) - - | Nest (_, j, doc) -> - pretty output state (indent + j) flatten doc cont - - | Group (req, doc) -> - (* If we already are in flattening mode, stay in flattening mode; we - are committed to it. If we are not already in flattening mode, we - have a choice of entering flattening mode. We enter this mode only - if we know that this group fits on this line without violating the - width or ribbon width constraints. Thus, we never backtrack. *) - let flatten = - flatten || - let column = state.column ++ req in - column <== state.width && column <== state.last_indent + state.ribbon - in - pretty output state indent flatten doc cont - - | Align (_, doc) -> - (* The effect of this combinator is to set [indent] to [state.column]. - Usually [indent] is equal to [state.last_indent], hence setting it - to [state.column] increases it. However, if [nest] has been used - since the current line began, then this could cause [indent] to - decrease. *) - (* assert (state.column > state.last_indent); *) - pretty output state state.column flatten doc cont - - | Range (_, hook, doc) -> - let start : point = (state.line, state.column) in - pretty output state indent flatten doc (KRange (hook, start, cont)) - - | Custom c -> - (* Invoke the document's custom rendering function. *) - c#pretty output state indent flatten; - (* Sanity check. *) - assert (ok state flatten); - (* Continue. *) - continue output state cont - -and continue output state = function - | KNil -> - () - | KCons (indent, flatten, doc, cont) -> - pretty output state indent flatten doc cont - | KRange (hook, start, cont) -> - let finish : point = (state.line, state.column) in - hook (start, finish); - continue output state cont - -(* Publish a version of [pretty] that does not take an explicit continuation. - This function may be used by authors of custom documents. We do not expose - the internal [pretty] -- the one that takes a continuation -- because we - wish to simplify the user's life. The price to pay is that calls that go - through a custom document cannot be tail calls. *) - -let pretty output state indent flatten doc = - pretty output state indent flatten doc KNil - -(* ------------------------------------------------------------------------- *) - -(* The compact rendering algorithm. *) - -let rec compact output doc cont = - match doc with - | Empty -> - continue output cont - | Char c -> - output#char c; - continue output cont - | String s -> - let len = String.length s in - output#substring s 0 len; - continue output cont - | FancyString (s, ofs, len, _apparent_length) -> - output#substring s ofs len; - continue output cont - | Blank n -> - blanks output n; - continue output cont - | HardLine -> - output#char '\n'; - continue output cont - | Cat (_, doc1, doc2) -> - compact output doc1 (doc2 :: cont) - | IfFlat (doc, _) - | Nest (_, _, doc) - | Group (_, doc) - | Align (_, doc) - | Range (_, _, doc) -> - compact output doc cont - | Custom c -> - (* Invoke the document's custom rendering function. *) - c#compact output; - continue output cont - -and continue output cont = - match cont with - | [] -> - () - | doc :: cont -> - compact output doc cont - -let compact output doc = - compact output doc [] - -(* ------------------------------------------------------------------------- *) - -(* We now instantiate the renderers for the three kinds of output channels. *) - -(* This is just boilerplate. *) - -module type RENDERER = sig - type channel - type document - val pretty: float -> int -> channel -> document -> unit - val compact: channel -> document -> unit -end - -module MakeRenderer (X : sig - type channel - val output: channel -> output -end) -: RENDERER with type channel = X.channel and type document = document -= struct - type channel = X.channel - type nonrec document = document - let pretty rfrac width channel doc = pretty (X.output channel) (initial rfrac width) 0 false doc - let compact channel doc = compact (X.output channel) doc -end - -module ToChannel = - MakeRenderer(struct - type channel = out_channel - let output channel = new buffering (new channel_output channel) - end) - -module ToBuffer = - MakeRenderer(struct - type channel = Buffer.t - let output buffer = new buffering (new buffer_output buffer) - end) - -module ToFormatter = - MakeRenderer(struct - type channel = Format.formatter - let output fmt = new buffering (new formatter_output fmt) - end) diff --git a/tests/menhir_tests/test10menhir/PPrintEngine.mli b/tests/menhir_tests/test10menhir/PPrintEngine.mli deleted file mode 100644 index 4d03b12..0000000 --- a/tests/menhir_tests/test10menhir/PPrintEngine.mli +++ /dev/null @@ -1,307 +0,0 @@ -(******************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2022 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(******************************************************************************) - -(**[PPrint] is an OCaml library for {b pretty-printing textual documents}. - It takes care of {b indentation and line breaks}, and is typically used - to {b pretty-print code}. *) - -(** {1:building Building Documents} *) - -(**The abstract type of documents. *) -type document - -(** {2 Atomic Documents} *) - -(**[empty] is the empty document. *) -val empty: document - -(**[char c] is an atomic document that consists of the single character [c]. - This character must not be a newline character. *) -val char: char -> document - -(**[string s] is an atomic document that consists of the string [s]. This - string must not contain a newline. The printing engine assumes that the - ideal width of this string is [String.length s]. This assumption is safe - if this is an ASCII string. Otherwise, {!fancystring} or {!utf8string} - should be preferred. *) -val string: string -> document - -(**[substring s ofs len] is an atomic document that consists of the portion - of the string [s] delimited by the offset [ofs] and the length [len]. - This portion must not contain a newline. [substring s ofs len] is - equivalent to [string (String.sub s ofs len)], but is expected to be more - efficient, as the substring is not actually extracted. *) -val substring: string -> int -> int -> document - -(**[fancystring s alen] is an atomic document that consists of the string - [s]. This string must not contain a newline. The string may contain fancy - characters: color escape characters, UTF-8 characters, etc. Thus, its - apparent length (which measures how many columns the text will take up on - screen) differs from its length in bytes. The printing engine assumes - that its apparent length is [alen]. *) -val fancystring: string -> int -> document - -(**[fancysubstring s ofs len alen] is equivalent to [fancystring (String.sub - s ofs len) alen]. *) -val fancysubstring : string -> int -> int -> int -> document - -(**[utf8string s] is an atomic document that consists of the UTF-8-encoded - string [s]. This string must not contain a newline. [utf8string s] is - equivalent to [fancystring s (utf8_length s)], where [utf8_length s] is - the apparent length of the UTF-8-encoded string [s]. *) -val utf8string: string -> document - -(** [utf8format format ...] is equivalent to - [utf8string (Printf.sprintf format ...)]. *) -val utf8format: ('a, unit, string, document) format4 -> 'a - -(** {2 Blanks and Newlines} *) - -(**The atomic document [hardline] represents a forced newline. This document - has infinite ideal width: thus, if there is a choice between printing it - in flat mode and printing it in normal mode, normal mode is preferred. In - other words, when [hardline] is placed directly inside a group, this - group is dissolved: [group hardline] is equivalent to [hardline]. This - combinator should be seldom used; consider using {!break} instead. *) -val hardline: document - -(**The atomic document [blank n] consists of [n] blank characters. A blank - character is like an ordinary ASCII space character [char ' '], except - that blank characters that appear at the end of a line are automatically - suppressed. *) -val blank: int -> document - -(**[space] is a synonym for [blank 1]. It consists of one blank character. - It is therefore not equivalent to [char ' ']. *) -val space: document - -(**The document [break n] is a breakable blank of width [n]. It produces [n] - blank characters if the printing engine is in flat mode, and a single - newline character if the printing engine is in normal mode. [break 1] is - equivalent to [ifflat (blank 1) hardline]. *) -val break: int -> document - -(** {2 Composite Documents} *) - -(**[doc1 ^^ doc2] is the concatenation of the documents [doc1] and [doc2]. *) -val (^^): document -> document -> document - -(**[group doc] encodes a choice. If the document [doc] fits on the current - line, then it is rendered on a single line, in flat mode. (All [group] - combinators inside it are then ignored.) Otherwise, this group is - dissolved, and [doc] is rendered in normal mode. There might be more - groups within [doc], whose presence leads to further choices being - explored. *) -val group: document -> document - -(**[ifflat doc1 doc2] is rendered as [doc1] if the printing engine is in - flat mode, that is, if the printing engine has determined that some - enclosing group fits on the current line. Otherwise, it is rendered as - [doc2]. Use this combinator with caution! Because the printing engine is - free to choose between [doc1] and [doc2], these documents must be - semantically equivalent. It is up to the user to enforce this property. *) -val ifflat: document -> document -> document - -(**To render the document [nest j doc], the printing engine temporarily - increases the current indentation level by [j], then renders [doc]. The - effect of the current indentation level is as follows: every time a - newline character is emitted, it is immediately followed by [n] blank - characters, where [n] is the current indentation level. Thus, one may - think of [nest j doc] roughly as the document [doc] in which [j] blank - characters have been inserted after every newline character. *) -val nest: int -> document -> document - -(**To render [align doc], the printing engine sets the current indentation - level to the current column, then renders [doc]. In other words, the - document [doc] is rendered within a box whose upper left corner is the - current position of the printing engine. *) -val align: document -> document - -(**A point is a pair of a line number and a column number. *) -type point = - int * int - -(**A range is a pair of points. *) -type range = - point * point - -(**The document [range hook doc] is printed like the document [doc], but - allows the caller to register a hook that is applied, when the document - is printed, to the range occupied by this document in the output text. - This offers a way of mapping positions in the output text back to - (sub)documents. *) -val range: (range -> unit) -> document -> document - -(** {1:rendering Rendering Documents} *) - -(**Three renderers are available. They offer the same API, described - by the signature {!RENDERER}, and differ only in the nature of the - output channel that they use. *) - -(**This signature describes the document renderers in a manner that - is independent of the type of the output channel. *) -module type RENDERER = sig - - (**The type of the output channel. *) - type channel - - (**The type of documents. *) - type document - - (** [pretty rfrac width channel document] pretty-prints the document - [document] into the output channel [channel]. The parameter [width] is - the maximum number of characters per line. The parameter [rfrac] is the - ribbon width, a fraction relative to [width]. The ribbon width is the - maximum number of non-indentation characters per line. *) - val pretty: float -> int -> channel -> document -> unit - - (** [compact channel document] prints the document [document] to the output - channel [channel]. No indentation is used. All newline instructions are - respected, that is, no groups are flattened. *) - val compact: channel -> document -> unit - -end - -(**This renderer sends its output into an output channel. *) -module ToChannel : RENDERER - with type channel = out_channel - and type document = document - -(**This renderer sends its output into a memory buffer. *) -module ToBuffer : RENDERER - with type channel = Buffer.t - and type document = document - -(**This renderer sends its output into a formatter channel. *) -module ToFormatter : RENDERER - with type channel = Format.formatter - and type document = document - -(** {1:defining Defining Custom Documents} *) - -(**It is possible to define custom document constructors, provided they meet - the expectations of the printing engine. In short, the custom document - combinator {!val-custom} expects an object of class {!class-type-custom}. - This object must provide three methods. The method [requirement] must - compute the ideal width of the custom document. The methods [pretty] and - [compact] must render the custom document. For this purpose, they have - access to the {{!output}output channel} and to the {{!state}state} of the - printing engine. *) - -(** A width requirement is expressed as an integer. The value [max_int] - is reserved and represents infinity. *) -type requirement = int - -(**[infinity] represents an infinite width requirement. *) -val infinity : requirement - -(**An output channel is abstractly represented as an object equipped with - methods for displaying one character and for displaying a substring. *) -class type output = object - - (**[char c] sends the character [c] to the output channel. *) - method char: char -> unit - - (**[substring s ofs len] sends the substring of [s] delimited by the - offset [ofs] and the length [len] to the output channel. *) - method substring: string -> int (* offset *) -> int (* length *) -> unit - -end - -(**The internal state of the rendering engine is exposed to the user who - wishes to define custom documents. However, its structure is subject to - change in future versions of the library. *) -type state = { - - width: int; - (** The line width. This parameter is fixed throughout the execution of - the renderer. *) - - ribbon: int; - (** The ribbon width. This parameter is fixed throughout the execution of - the renderer. *) - - mutable last_indent: int; - (** The number of blanks that were printed at the beginning of the current - line. This field is updated (only) when a hardline is emitted. It is - used (only) to determine whether the ribbon width constraint is - respected. *) - - mutable line: int; - (** The current line. This field is updated (only) when a hardline is - emitted. It is not used by the pretty-printing engine itself. *) - - mutable column: int; - (** The current column. This field must be updated whenever something is - sent to the output channel. It is used (only) to determine whether the - width constraint is respected. *) - - } - -(**A custom document is defined by implementing an object of class - {!class-type-custom}. *) -class type custom = object - - (**A custom document must publish the width (i.e., the number of columns) - that it would like to occupy if printed on a single line (in flat - mode). The special value [infinity] means that this document cannot be - printed on a single line; this value causes any groups that contain - this document to be dissolved. This method should in principle work in - constant time. *) - method requirement: requirement - - (**The method [pretty] is used by the main rendering algorithm. It has - access to the output channel and to the printing engine's internal - state. In addition, it receives the current indentation level and a - Boolean flag that tells whether the engine is currently in flat mode. - If the engine is in flat mode, then the document must be printed on a - single line, in a manner that is consistent with the width requirement - that was published ahead of time. If the engine is in normal mode, then - there is no such obligation. The state must be updated in a manner that - is consistent with what is sent to the output channel. *) - method pretty: output -> state -> int -> bool -> unit - - (**The method [compact] is used by the compact rendering algorithm. It - has access to the output channel only. *) - method compact: output -> unit - -end - -(**[custom] constructs a custom document out an object of type - {!class-type-custom}. *) -val custom: custom -> document - -(**Some of the key functions of the library are exposed, in the hope that - they may be useful to authors of custom (leaf and composite) documents. - In the case of a leaf document, they can help perform certain basic - functions; for instance, applying the function {!pretty} to the document - {!hardline} is a simple way of printing a hardline, while respecting the - indentation parameters and updating the state in a correct manner. - Similarly, applying {!pretty} to the document [blank n] is a simple way - of printing [n] blank characters. In the case of a composite document - (one that contains subdocuments), these functions are essential: they - allow computing the width requirement of a subdocument and displaying a - subdocument. *) - -(**[requirement doc] computes the width requirement of the document [doc]. - It runs in constant time. *) -val requirement: document -> requirement - -(**[pretty output state indent flatten doc] prints the document [doc]. See - the documentation of the method [pretty] in the class - {!class-type-custom}. *) -val pretty: output -> state -> int -> bool -> document -> unit - -(**[compact output doc] prints the document [doc]. See the documentation of - the method [compact] in the class {!class-type-custom}. *) -val compact: output -> document -> unit diff --git a/tests/menhir_tests/test10menhir_with_aux_args/Makefile b/tests/menhir_tests/test10menhir_with_aux_args/Makefile index 78f8ae9..f26a025 100644 --- a/tests/menhir_tests/test10menhir_with_aux_args/Makefile +++ b/tests/menhir_tests/test10menhir_with_aux_args/Makefile @@ -8,7 +8,7 @@ MENHIR := menhir MENHIRFLAGS := --infer -OCAMLBUILD := ocamlbuild -use-ocamlfind -use-menhir -menhir "$(MENHIR) $(MENHIRFLAGS)" +OCAMLBUILD := ocamlbuild -use-ocamlfind -use-menhir -menhir "$(MENHIR) $(MENHIRFLAGS)" -package pprint MAIN := main diff --git a/tests/menhir_tests/test10menhir_with_aux_args/PPrint.ml b/tests/menhir_tests/test10menhir_with_aux_args/PPrint.ml deleted file mode 100644 index da55896..0000000 --- a/tests/menhir_tests/test10menhir_with_aux_args/PPrint.ml +++ /dev/null @@ -1,468 +0,0 @@ -(******************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2022 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(******************************************************************************) - -include PPrintEngine - -(* -------------------------------------------------------------------------- *) - -(* Predefined single-character documents. *) - -let lparen = char '(' -let rparen = char ')' -let langle = char '<' -let rangle = char '>' -let lbrace = char '{' -let rbrace = char '}' -let lbracket = char '[' -let rbracket = char ']' -let squote = char '\'' -let dquote = char '"' -let bquote = char '`' -let semi = char ';' -let colon = char ':' -let comma = char ',' -let dot = char '.' -let sharp = char '#' -let slash = char '/' -let backslash = char '\\' -let equals = char '=' -let qmark = char '?' -let tilde = char '~' -let at = char '@' -let percent = char '%' -let dollar = char '$' -let caret = char '^' -let ampersand = char '&' -let star = char '*' -let plus = char '+' -let minus = char '-' -let underscore = char '_' -let bang = char '!' -let bar = char '|' - -(* -------------------------------------------------------------------------- *) - -(* Repetition. *) - -let twice doc = - doc ^^ doc - -let repeat n doc = - let rec loop n doc accu = - if n = 0 then - accu - else - loop (n - 1) doc (doc ^^ accu) - in - loop n doc empty - -(* -------------------------------------------------------------------------- *) - -(* Delimiters. *) - -let precede l x = l ^^ x -let terminate r x = x ^^ r -let enclose l r x = l ^^ x ^^ r - -let squotes = enclose squote squote -let dquotes = enclose dquote dquote -let bquotes = enclose bquote bquote -let braces = enclose lbrace rbrace -let parens = enclose lparen rparen -let angles = enclose langle rangle -let brackets = enclose lbracket rbracket - -(* -------------------------------------------------------------------------- *) - -(* Some functions on lists. *) - -(* A variant of [fold_left] that keeps track of the element index. *) - -let foldli (f : int -> 'b -> 'a -> 'b) (accu : 'b) (xs : 'a list) : 'b = - let r = ref 0 in - List.fold_left (fun accu x -> - let i = !r in - r := i + 1; - f i accu x - ) accu xs - -(* -------------------------------------------------------------------------- *) - -(* Working with lists of documents. *) - -let concat docs = - (* We take advantage of the fact that [^^] operates in constant - time, regardless of the size of its arguments. The document - that is constructed is essentially a reversed list (i.e., a - tree that is biased towards the left). This is not a problem; - when pretty-printing this document, the engine will descend - along the left branch, pushing the nodes onto its stack as - it goes down, effectively reversing the list again. *) - List.fold_left (^^) empty docs - -let separate sep docs = - foldli (fun i accu doc -> - if i = 0 then - doc - else - accu ^^ sep ^^ doc - ) empty docs - -let concat_map f xs = - List.fold_left (fun accu x -> - accu ^^ f x - ) empty xs - -let separate_map sep f xs = - foldli (fun i accu x -> - if i = 0 then - f x - else - accu ^^ sep ^^ f x - ) empty xs - -let separate2 sep last_sep docs = - let n = List.length docs in - foldli (fun i accu doc -> - if i = 0 then - doc - else - accu ^^ (if i < n - 1 then sep else last_sep) ^^ doc - ) empty docs - -let optional f = function - | None -> - empty - | Some x -> - f x - -(* -------------------------------------------------------------------------- *) - -(* Text. *) - -(* This variant of [String.index_from] returns an option. *) - -let index_from s i c = - try - Some (String.index_from s i c) - with Not_found -> - None - -(* [lines s] chops the string [s] into a list of lines, which are turned - into documents. *) - -let lines s = - let rec chop accu i = - match index_from s i '\n' with - | Some j -> - let accu = substring s i (j - i) :: accu in - chop accu (j + 1) - | None -> - substring s i (String.length s - i) :: accu - in - List.rev (chop [] 0) - -let arbitrary_string s = - separate (break 1) (lines s) - -(* [split ok s] splits the string [s] at every occurrence of a character - that satisfies the predicate [ok]. The substrings thus obtained are - turned into documents, and a list of documents is returned. No information - is lost: the concatenation of the documents yields the original string. - This code is not UTF-8 aware. *) - -let split ok s = - let n = String.length s in - let rec index_from i = - if i = n then - None - else if ok s.[i] then - Some i - else - index_from (i + 1) - in - let rec chop accu i = - match index_from i with - | Some j -> - let accu = substring s i (j - i) :: accu in - let accu = char s.[j] :: accu in - chop accu (j + 1) - | None -> - substring s i (String.length s - i) :: accu - in - List.rev (chop [] 0) - -(* [words s] chops the string [s] into a list of words, which are turned - into documents. *) - -let words s = - let n = String.length s in - (* A two-state finite automaton. *) - (* In this state, we have skipped at least one blank character. *) - let rec skipping accu i = - if i = n then - (* There was whitespace at the end. Drop it. *) - accu - else match s.[i] with - | ' ' - | '\t' - | '\n' - | '\r' -> - (* Skip more whitespace. *) - skipping accu (i + 1) - | _ -> - (* Begin a new word. *) - word accu i (i + 1) - (* In this state, we have skipped at least one non-blank character. *) - and word accu i j = - if j = n then - (* Final word. *) - substring s i (j - i) :: accu - else match s.[j] with - | ' ' - | '\t' - | '\n' - | '\r' -> - (* A new word has been identified. *) - let accu = substring s i (j - i) :: accu in - skipping accu (j + 1) - | _ -> - (* Continue inside the current word. *) - word accu i (j + 1) - in - List.rev (skipping [] 0) - -let flow_map sep f docs = - foldli (fun i accu doc -> - if i = 0 then - f doc - else - accu ^^ - (* This idiom allows beginning a new line if [doc] does not - fit on the current line. *) - group (sep ^^ f doc) - ) empty docs - -let flow sep docs = - flow_map sep (fun x -> x) docs - -let url s = - flow (break 0) (split (function '/' | '.' -> true | _ -> false) s) - -(* -------------------------------------------------------------------------- *) -(* Alignment and indentation. *) - -let hang i d = - align (nest i d) - -let ( !^ ) = string - -let ( ^/^ ) x y = - x ^^ break 1 ^^ y - -let prefix n b x y = - group (x ^^ nest n (break b ^^ y)) - -let (^//^) = - prefix 2 1 - -let jump n b y = - group (nest n (break b ^^ y)) - -let infix n b op x y = - prefix n b (x ^^ blank b ^^ op) y - -let surround n b opening contents closing = - group (opening ^^ nest n ( break b ^^ contents) ^^ break b ^^ closing ) - -let soft_surround n b opening contents closing = - group (opening ^^ nest n (group (break b) ^^ contents) ^^ group (break b ^^ closing)) - -let surround_separate n b void opening sep closing docs = - match docs with - | [] -> - void - | _ :: _ -> - surround n b opening (separate sep docs) closing - -let surround_separate_map n b void opening sep closing f xs = - match xs with - | [] -> - void - | _ :: _ -> - surround n b opening (separate_map sep f xs) closing - -(* -------------------------------------------------------------------------- *) -(* Printing OCaml values. *) - -module OCaml = struct - -open Printf - -type constructor = string -type type_name = string -type record_field = string -type tag = int - -(* -------------------------------------------------------------------------- *) - -(* This internal [sprintf]-like function produces a document. We use [string], - as opposed to [arbitrary_string], because the strings that we produce will - never contain a newline character. *) - -let dsprintf format = - ksprintf string format - -(* -------------------------------------------------------------------------- *) - -(* Nicolas prefers using this code as opposed to just [sprintf "%g"] or - [sprintf "%f"]. The latter print [inf] and [-inf], whereas OCaml - understands [infinity] and [neg_infinity]. [sprintf "%g"] does not add a - trailing dot when the number happens to be an integral number. [sprintf - "%F"] seems to lose precision and ignores the precision modifier. *) - -let valid_float_lexeme (s : string) : string = - let l = String.length s in - let rec loop i = - if i >= l then - (* If we reach the end of the string and have found only characters in - the set '0' .. '9' and '-', then this string will be considered as an - integer literal by OCaml. Adding a trailing dot makes it a float - literal. *) - s ^ "." - else - match s.[i] with - | '0' .. '9' | '-' -> loop (i + 1) - | _ -> s - in loop 0 - -(* This function constructs a string representation of a floating point - number. This representation is supposed to be accepted by OCaml as a - valid floating point literal. *) - -let float_representation (f : float) : string = - match classify_float f with - | FP_nan -> - "nan" - | FP_infinite -> - if f < 0.0 then "neg_infinity" else "infinity" - | _ -> - (* Try increasing precisions and validate. *) - let s = sprintf "%.12g" f in - if f = float_of_string s then valid_float_lexeme s else - let s = sprintf "%.15g" f in - if f = float_of_string s then valid_float_lexeme s else - sprintf "%.18g" f - -(* -------------------------------------------------------------------------- *) - -(* A few constants and combinators, used below. *) - -let some = - string "Some" - -let none = - string "None" - -let lbracketbar = - string "[|" - -let rbracketbar = - string "|]" - -let seq1 opening separator closing = - surround_separate 2 0 - (opening ^^ closing) opening (separator ^^ break 1) closing - -let seq2 opening separator closing = - surround_separate_map 2 1 - (opening ^^ closing) opening (separator ^^ break 1) closing - -(* -------------------------------------------------------------------------- *) - -(* The following functions are printers for many types of OCaml values. *) - -(* There is no protection against cyclic values. *) - -let tuple = - seq1 lparen comma rparen - -let variant _ cons _ args = - match args with - | [] -> - !^cons - | _ :: _ -> - !^cons ^^ tuple args - -let record _ fields = - seq2 lbrace semi rbrace (fun (k, v) -> infix 2 1 equals !^k v) fields - -let option f = function - | None -> - none - | Some x -> - some ^^ tuple [f x] - -let list f xs = - seq2 lbracket semi rbracket f xs - -let flowing_list f xs = - group (lbracket ^^ space ^^ nest 2 ( - flow_map (semi ^^ break 1) f xs - ) ^^ space ^^ rbracket) - -let array f xs = - seq2 lbracketbar semi rbracketbar f (Array.to_list xs) - -let flowing_array f xs = - group (lbracketbar ^^ space ^^ nest 2 ( - flow_map (semi ^^ break 1) f (Array.to_list xs) - ) ^^ space ^^ rbracketbar) - -let ref f x = - record "ref" ["contents", f !x] - -let float f = - string (float_representation f) - -let int = - dsprintf "%d" - -let int32 = - dsprintf "%ld" - -let int64 = - dsprintf "%Ld" - -let nativeint = - dsprintf "%nd" - -let char = - dsprintf "%C" - -let bool = - dsprintf "%B" - -let unit = - dsprintf "()" - -let string = - dsprintf "%S" - -let unknown tyname _ = - dsprintf "" tyname - -type representation = - document - -end (* OCaml *) diff --git a/tests/menhir_tests/test10menhir_with_aux_args/PPrint.mli b/tests/menhir_tests/test10menhir_with_aux_args/PPrint.mli deleted file mode 100644 index 89eeeeb..0000000 --- a/tests/menhir_tests/test10menhir_with_aux_args/PPrint.mli +++ /dev/null @@ -1,345 +0,0 @@ -(******************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2022 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(******************************************************************************) - -include module type of PPrintEngine (** @inline *) - -(** {1:combinators High-Level Combinators} *) - -(** {2 Single Characters} *) - -(**The following atomic documents consist of a single character. Each of - them is a synonym for the application of {!char} to some constant - character. For instance, {!lparen} is a synonym for [char '(']. *) - -val lparen: document -val rparen: document -val langle: document -val rangle: document -val lbrace: document -val rbrace: document -val lbracket: document -val rbracket: document -val squote: document -val dquote: document -val bquote: document -val semi: document -val colon: document -val comma: document -val dot: document -val sharp: document -val slash: document -val backslash: document -val equals: document -val qmark: document -val tilde: document -val at: document -val percent: document -val dollar: document -val caret: document -val ampersand: document -val star: document -val plus: document -val minus: document -val underscore: document -val bang: document -val bar: document - -(** {2 Delimiters} *) - -(**[precede l x] is [l ^^ x]. *) -val precede: document -> document -> document - -(**[terminate r x] is [x ^^ r]. *) -val terminate: document -> document -> document - -(**[enclose l r x] is [l ^^ x ^^ r]. *) -val enclose: document -> document -> document -> document - -(**The following combinators enclose a document within a pair of delimiters. - They are partial applications of [enclose]. No whitespace or line break - is introduced. *) - -val squotes: document -> document -val dquotes: document -> document -val bquotes: document -> document -val braces: document -> document -val parens: document -> document -val angles: document -> document -val brackets: document -> document - -(** {2 Repetition} *) - -(**[twice doc] is the document obtained by concatenating two copies of - the document [doc]. *) -val twice: document -> document - -(**[repeat n doc] is the document obtained by concatenating [n] copies of - the document [doc]. *) -val repeat: int -> document -> document - -(** {2 Lists and Options} *) - -(**[concat docs] is the concatenation of the documents in the list [docs]. *) -val concat: document list -> document - -(**[separate sep docs] is the concatenation of the documents in the list - [docs]. The separator [sep] is inserted between every two adjacent - documents. *) -val separate: document -> document list -> document - -(**[concat_map f xs] is equivalent to [concat (List.map f xs)]. *) -val concat_map: ('a -> document) -> 'a list -> document - -(**[separate_map sep f xs] is equivalent to [separate sep (List.map f xs)]. *) -val separate_map: document -> ('a -> document) -> 'a list -> document - -(**[separate2 sep last_sep docs] is the concatenation of the documents in - the list [docs]. The separator [sep] is inserted between every two - adjacent documents, except between the last two documents, where the - separator [last_sep] is used instead. *) -val separate2: document -> document -> document list -> document - -(**[optional f None] is the empty document. [optional f (Some x)] is - the document [f x]. *) -val optional: ('a -> document) -> 'a option -> document - -(** {2 Text} *) - -(**[lines s] is the list of documents obtained by splitting [s] at newline - characters, and turning each line into a document via [substring]. This - code is not UTF-8 aware. *) -val lines: string -> document list - -(**[arbitrary_string s] is equivalent to [separate (break 1) (lines s)]. - It is analogous to [string s], but is valid even if the string [s] - contains newline characters. *) -val arbitrary_string: string -> document - -(**[words s] is the list of documents obtained by splitting [s] at whitespace - characters, and turning each word into a document via [substring]. All - whitespace is discarded. This code is not UTF-8 aware. *) -val words: string -> document list - -(**[split ok s] splits the string [s] before and after every occurrence of a - character that satisfies the predicate [ok]. The substrings thus obtained - are turned into documents, and a list of documents is returned. No - information is lost: the concatenation of the documents yields the - original string. This code is not UTF-8 aware. *) -val split: (char -> bool) -> string -> document list - -(**[flow sep docs] separates the documents in the list [docs] with the - separator [sep] and arranges for a new line to begin whenever a document - does not fit on the current line. This is useful for typesetting - free-flowing, ragged-right text. A typical choice of [sep] is [break b], - where [b] is the number of spaces that must be inserted between two - consecutive words (when displayed on the same line). *) -val flow: document -> document list -> document - -(**[flow_map sep f docs] is equivalent to [flow sep (List.map f docs)]. *) -val flow_map: document -> ('a -> document) -> 'a list -> document - -(**[url s] is a possible way of displaying the URL [s]. A potential line - break is inserted immediately before and immediately after every slash - and dot character. *) -val url: string -> document - -(** {2 Alignment and Indentation} *) - -(**[hang n doc] is analogous to [align], but additionally indents all lines, - except the first one, by [n]. Thus, the text in the box forms a hanging - indent. *) -val hang: int -> document -> document - -(**[prefix n b left right] has the following flat layout: - {[ - left right - ]} - and the following non-flat layout: - {[ - left - right - ]} - The parameter [n] controls the nesting of [right] (when not flat). - The parameter [b] controls the number of spaces between [left] and [right] - (when flat). *) -val prefix: int -> int -> document -> document -> document - -(**[jump n b right] is equivalent to [prefix n b empty right]. *) -val jump: int -> int -> document -> document - -(**[infix n b middle left right] has the following flat layout: - {[ - left middle right - ]} - and the following non-flat layout: - {[ - left middle - right - ]} - The parameter [n] controls the nesting of [right] (when not flat). - The parameter [b] controls the number of spaces between [left] and [middle] - (always) and between [middle] and [right] (when flat). *) -val infix: int -> int -> document -> document -> document -> document - -(**[surround n b opening contents closing] has the following flat layout: - {[ - opening contents closing - ]} - and the following non-flat layout: - {[ - opening - contents - closing - ]} - The parameter [n] controls the nesting of [contents] (when not flat). - The parameter [b] controls the number of spaces between [opening] and - [contents] and between [contents] and [closing] (when flat). -*) -val surround: int -> int -> document -> document -> document -> document - -(**[soft_surround] is analogous to [surround], but involves more than one - group, so it offers possibilities other than the completely flat layout - (where [opening], [contents], and [closing] appear on a single line) and - the completely developed layout (where [opening], [contents], and - [closing] appear on separate lines). It tries to place the beginning of - [contents] on the same line as [opening], and to place [closing] on the - same line as the end of [contents], if possible. *) -val soft_surround: int -> int -> document -> document -> document -> document - -(**[surround_separate n b void opening sep closing docs] is equivalent to - [surround n b opening (separate sep docs) closing], except when the list - [docs] is empty, in which case it reduces to [void]. *) -val surround_separate: - int -> int -> - document -> document -> document -> document -> - document list -> document - -(**[surround_separate_map n b void opening sep closing f xs] is equivalent - to [surround_separate n b void opening sep closing (List.map f xs)]. *) -val surround_separate_map: - int -> int -> - document -> document -> document -> document -> - ('a -> document) -> 'a list -> document - -(** {2 Short-Hands} *) - -(**[!^s] is a short-hand for [string s]. *) -val ( !^ ) : string -> document - -(**[x ^/^ y] separates [x] and [y] with a breakable space. - It is a short-hand for [x ^^ break 1 ^^ y]. *) -val ( ^/^ ) : document -> document -> document - -(**[x ^//^ y] is a short-hand for [prefix 2 1 x y]. *) -val ( ^//^ ) : document -> document -> document - -(** {1:ocaml Printing OCaml Values} *) - -(**This module offers document combinators that help print OCaml values. The - strings produced by rendering these documents are supposed to be accepted - by the OCaml parser as valid values. - - These functions do {i not} distinguish between mutable and immutable - values. They do {i not} recognize sharing, and do {i not} incorporate a - protection against cyclic values. *) -module OCaml : sig - -(* The signature of this module is compatible with that expected by the - [camlp4] generator [Camlp4RepresentationGenerator]. This explains why - some functions have unused parameters. This is also the reason why - there is a type [representation]. *) - -type constructor = string -type type_name = string -type record_field = string -type tag = int - -(**[variant _ dc _ args] represents a constructed value whose data - constructor is [dc] and whose arguments are [args]. The other two - parameters are presently unused. *) -val variant : type_name -> constructor -> tag -> document list -> document - -(**[record _ fields] represents a record value whose fields are [fields]. - The other parameter is presently unused. *) -val record : type_name -> (record_field * document) list -> document - -(**[tuple args] represents a tuple value whose components are [args]. *) -val tuple : document list -> document - -(**[string s] represents the literal string [s]. *) -val string : string -> document - -(**[int i] represents the literal integer [i]. *) -val int : int -> document - -(**[int32 i] represents the literal 32-bit integer [i]. *) -val int32 : int32 -> document - -(**[int64 i] represents the literal 64-bit integer [i]. *) -val int64 : int64 -> document - -(**[nativeint i] represents the literal native integer [i]. *) -val nativeint : nativeint -> document - -(**[float f] represents the literal floating-point number [f]. *) -val float : float -> document - -(**[char c] represents the literal character [c]. *) -val char : char -> document - -(**[bool b] represents the Boolean value [b]. *) -val bool : bool -> document - -(**[unit] represents the unit constant [()]. *) -val unit : document - -(**[option f o] represents the option [o]. The representation of the - element, if present, is computed by the function [f]. *) -val option : ('a -> document) -> 'a option -> document - -(**[list f xs] represents the list [xs]. The representation of each element - is computed by the function [f]. If the whole list fits on a single line, - then it is printed on a single line; otherwise each element is printed on - a separate line. *) -val list : ('a -> document) -> 'a list -> document - -(**[flowing_list f xs] represents the list [xs]. The representation of each - element is computed by the function [f]. As many elements are possible - are printed on each line. *) -val flowing_list : ('a -> document) -> 'a list -> document - -(**[array f xs] represents the array [xs]. The representation of each - element is computed by the function [f]. If the whole array fits on a - single line, then it is printed on a single line; otherwise each element - is printed on a separate line. *) -val array : ('a -> document) -> 'a array -> document - -(**[flowing_array f xs] represents the array [xs]. The representation of - each element is computed by the function [f]. As many elements are - possible are printed on each line. *) -val flowing_array : ('a -> document) -> 'a array -> document - -(**[ref r] represents the reference [r]. The representation of the content - is computed by the function [f]. *) -val ref : ('a -> document) -> 'a ref -> document - -(** [unknown t _] represents an unknown value of type [t]. It is rendered - as a string of the form []. *) -val unknown : type_name -> 'a -> document - -(**/**) - -type representation = - document - -end (* OCaml *) diff --git a/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.ml b/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.ml deleted file mode 100644 index 3131893..0000000 --- a/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.ml +++ /dev/null @@ -1,751 +0,0 @@ -(******************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2022 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(******************************************************************************) - -(** A point is a pair of a line number and a column number. *) -type point = - int * int - -(** A range is a pair of points. *) -type range = - point * point - -(* ------------------------------------------------------------------------- *) - -(* A type of integers with infinity. *) - -type requirement = - int (* with infinity *) - -(* Infinity is encoded as [max_int]. *) - -let infinity : requirement = - max_int - -(* Addition of integers with infinity. *) - -let (++) (x : requirement) (y : requirement) : requirement = - if x = infinity || y = infinity then - infinity - else - x + y - -(* Comparison between an integer with infinity and a normal integer. *) - -let (<==) (x : requirement) (y : int) = - x <= y - -(* ------------------------------------------------------------------------- *) - -(* A uniform interface for output channels. *) - -class type output = object - - (** [char c] sends the character [c] to the output channel. *) - method char: char -> unit - - (** [substring s ofs len] sends the substring of [s] delimited by the - offset [ofs] and the length [len] to the output channel. *) - method substring: string -> int (* offset *) -> int (* length *) -> unit - -end - -(* ------------------------------------------------------------------------- *) - -(* Printing blank space. This is used both internally (to emit indentation - characters) and via the public combinator [blank]. *) - -let blank_length = - 80 - -let blank_buffer = - String.make blank_length ' ' - -let rec blanks (output : output) n = - if n <= 0 then - () - else if n <= blank_length then - output#substring blank_buffer 0 n - else begin - output#substring blank_buffer 0 blank_length; - blanks output (n - blank_length) - end - -(* ------------------------------------------------------------------------- *) - -(* The class [buffering] implements a wrapper that delays the printing of - blank characters. This includes indentation characters and characters - produced by the combinator [blank]. The printing of these characters is - delayed until it is known that they are followed by something on the same - line; if they are not followed with anything, then it is canceled. - - The actual printing task is delegated to the object [delegate], whose type - is [output]; the new object has type [output] as well. *) - -class buffering (delegate : output) : output = object (self) - - (* The number of blank characters that are withholding. *) - val mutable buffered = 0 - - (* [flush] sends out the blank characters that have been withheld. *) - method private flush = - blanks delegate buffered; - buffered <- 0 - - method char c : unit = - begin match c with - | '\n' -> - (* The current line ends here. Any blank characters that were withheld - are destroyed. This is where we avoid printing blank characters if - nothing follows them. *) - buffered <- 0 - | _ -> - (* The current line is nonempty. Any blank characters that were - withheld can now be flushed. *) - self#flush - end; - (* Print this character as usual. *) - delegate#char c - - method substring s pos len = - (* If this is a string of length zero, then there is nothing to do. *) - if len = 0 then - () - (* If this is a blank string (which we recognize by its address), then - its content is withheld. *) - else if s == blank_buffer then - buffered <- buffered + len - (* If this is not a blank string, then the blank characters that were - withheld up to this point can now be flushed. *) - else begin - self#flush; - delegate#substring s pos len - end - -end - -(* ------------------------------------------------------------------------- *) - -(* Three kinds of output channels are wrapped so as to satisfy the above - interface: OCaml output channels, OCaml memory buffers, and OCaml - formatters. *) - -class channel_output channel = object - method char = output_char channel - method substring = output_substring channel - (* We used to use [output], but, as of OCaml 4.02 and with -safe-string - enabled, the type of [output] has changed: this function now expects - an argument of type [bytes]. The new function [output_substring] must - be used instead. Furthermore, as of OCaml 4.06, -safe-string is enabled - by default. In summary, we require OCaml 4.02, use [output_substring], - and enable -safe-string. *) -end - -class buffer_output buffer = object - method char = Buffer.add_char buffer - method substring = Buffer.add_substring buffer -end - -class formatter_output fmt = object - method char = function - | '\n' -> Format.pp_force_newline fmt () - | ' ' -> Format.pp_print_space fmt () - | c -> Format.pp_print_char fmt c - - method substring str ofs len = - Format.pp_print_text fmt ( - if ofs = 0 && len = String.length str - then str - else String.sub str ofs len - ) -end - -(* ------------------------------------------------------------------------- *) - -(** The rendering engine maintains the following internal state. Its structure - is subject to change in future versions of the library. Nevertheless, it is - exposed to the user who wishes to define custom documents. *) - -type state = { - - width: int; - (** The line width. This parameter is fixed throughout the execution of - the renderer. *) - - ribbon: int; - (** The ribbon width. This parameter is fixed throughout the execution of - the renderer. *) - - mutable last_indent: int; - (** The number of blanks that were printed at the beginning of the current - line. This field is updated (only) when a hardline is emitted. It is - used (only) to determine whether the ribbon width constraint is - respected. *) - - mutable line: int; - (** The current line. This field is updated (only) when a hardline is - emitted. It is not used by the pretty-printing engine itself. *) - - mutable column: int; - (** The current column. This field must be updated whenever something is - sent to the output channel. It is used (only) to determine whether the - width constraint is respected. *) - - } - -(* ------------------------------------------------------------------------- *) - -(* [initial rfrac width] creates a fresh initial state. *) - -let initial rfrac width = { - width = width; - ribbon = max 0 (min width (truncate (float_of_int width *. rfrac))); - last_indent = 0; - line = 0; - column = 0 -} - -(* ------------------------------------------------------------------------- *) - -(** A custom document is defined by implementing the following methods. *) - -class type custom = object - - (** A custom document must publish the width (i.e., the number of columns) - that it would like to occupy if it is printed on a single line (that is, - in flattening mode). The special value [infinity] means that this - document cannot be printed on a single line; this value causes any - groups that contain this document to be dissolved. This method should - in principle work in constant time. *) - method requirement: requirement - - (** The method [pretty] is used by the main rendering algorithm. It has - access to the output channel and to the algorithm's internal state, as - described above. In addition, it receives the current indentation level - and the current flattening mode (on or off). If flattening mode is on, - then the document must be printed on a single line, in a manner that is - consistent with the requirement that was published ahead of time. If - flattening mode is off, then there is no such obligation. The state must - be updated in a manner that is consistent with what is sent to the - output channel. *) - method pretty: output -> state -> int -> bool -> unit - - (** The method [compact] is used by the compact rendering algorithm. It has - access to the output channel only. *) - method compact: output -> unit - -end - -(* ------------------------------------------------------------------------- *) - -(* Here is the algebraic data type of documents. It is analogous to Daan - Leijen's version, but the binary constructor [Union] is replaced with - the unary constructor [Group], and the constant [Line] is replaced with - more general constructions, namely [IfFlat], which provides alternative - forms depending on the current flattening mode, and [HardLine], which - represents a newline character, and causes a failure in flattening mode. *) - -type document = - - (* [Empty] is the empty document. *) - - | Empty - - (* [Char c] is a document that consists of the single character [c]. We - enforce the invariant that [c] is not a newline character. *) - - | Char of char - - (* [String s] is a document that consists of just the string [s]. We - assume, but do not check, that this string does not contain a newline - character. [String] is a special case of [FancyString], which takes up - less space in memory. *) - - | String of string - - (* [FancyString (s, ofs, len, apparent_length)] is a (portion of a) string - that may contain fancy characters: color escape characters, UTF-8 or - multi-byte characters, etc. Thus, the apparent length (which corresponds - to what will be visible on screen) differs from the length (which is a - number of bytes, and is reported by [String.length]). We assume, but do - not check, that fancystrings do not contain a newline character. *) - - | FancyString of string * int * int * int - - (* [Blank n] is a document that consists of [n] blank characters. *) - - | Blank of int - - (* When in flattening mode, [IfFlat (d1, d2)] turns into the document - [d1]. When not in flattening mode, it turns into the document [d2]. *) - - | IfFlat of document * document - - (* When in flattening mode, [HardLine] causes a failure, which requires - backtracking all the way until the stack is empty. When not in flattening - mode, it represents a newline character, followed with an appropriate - number of indentation. A common way of using [HardLine] is to only use it - directly within the right branch of an [IfFlat] construct. *) - - | HardLine - - (* The following constructors store their space requirement. This is the - document's apparent length, if printed in flattening mode. This - information is computed in a bottom-up manner when the document is - constructed. *) - - (* In other words, the space requirement is the number of columns that the - document needs in order to fit on a single line. We express this value in - the set of `integers extended with infinity', and use the value - [infinity] to indicate that the document cannot be printed on a single - line. *) - - (* Storing this information at [Group] nodes is crucial, as it allows us to - avoid backtracking and buffering. *) - - (* Storing this information at other nodes allows the function [requirement] - to operate in constant time. This means that the bottom-up computation of - requirements takes linear time. *) - - (* [Cat (req, doc1, doc2)] is the concatenation of the documents [doc1] and - [doc2]. The space requirement [req] is the sum of the requirements of - [doc1] and [doc2]. *) - - | Cat of requirement * document * document - - (* [Nest (req, j, doc)] is the document [doc], in which the indentation - level has been increased by [j], that is, in which [j] blanks have been - inserted after every newline character. The space requirement [req] is - the same as the requirement of [doc]. *) - - | Nest of requirement * int * document - - (* [Group (req, doc)] represents an alternative: it is either a flattened - form of [doc], in which occurrences of [Group] disappear and occurrences - of [IfFlat] resolve to their left branch, or [doc] itself. The space - requirement [req] is the same as the requirement of [doc]. *) - - | Group of requirement * document - - (* [Align (req, doc)] increases the indentation level to reach the current - column. Thus, the document [doc] is rendered within a box whose upper - left corner is the current position. The space requirement [req] is the - same as the requirement of [doc]. *) - - | Align of requirement * document - - (* [Range (req, hook, doc)] is printed like [doc]. After it is printed, the - function [hook] is applied to the range that is occupied by [doc] in the - output. *) - - | Range of requirement * (range -> unit) * document - - (* [Custom (req, f)] is a document whose appearance is user-defined. *) - - | Custom of custom - -(* ------------------------------------------------------------------------- *) - -(* Retrieving or computing the space requirement of a document. *) - -let rec requirement = function - | Empty -> - 0 - | Char _ -> - 1 - | String s -> - String.length s - | FancyString (_, _, _, len) - | Blank len -> - len - | IfFlat (doc1, _) -> - (* In flattening mode, the requirement of [ifflat x y] is just the - requirement of its flat version, [x]. *) - (* The smart constructor [ifflat] ensures that [IfFlat] is never nested - in the left-hand side of [IfFlat], so this recursive call is not a - problem; the function [requirement] has constant time complexity. *) - requirement doc1 - | HardLine -> - (* A hard line cannot be printed in flattening mode. *) - infinity - | Cat (req, _, _) - | Nest (req, _, _) - | Group (req, _) - | Align (req, _) - | Range (req, _, _) -> - (* These nodes store their requirement -- which is computed when the - node is constructed -- so as to allow us to answer in constant time - here. *) - req - | Custom c -> - c#requirement - -(* ------------------------------------------------------------------------- *) - -(* The above algebraic data type is not exposed to the user. Instead, we - expose the following functions. These functions construct a raw document - and compute its requirement, so as to obtain a document. *) - -let empty = - Empty - -let char c = - assert (c <> '\n'); - Char c - -let space = - Blank 1 - -let string s = - String s - -let fancysubstring s ofs len apparent_length = - if len = 0 then - empty - else - FancyString (s, ofs, len, apparent_length) - -let substring s ofs len = - fancysubstring s ofs len len - -let fancystring s apparent_length = - fancysubstring s 0 (String.length s) apparent_length - -(* The following function was stolen from [Batteries]. *) -let utf8_length s = - let rec length_aux s c i = - if i >= String.length s then c else - let n = Char.code (String.unsafe_get s i) in - let k = - if n < 0x80 then 1 else - if n < 0xe0 then 2 else - if n < 0xf0 then 3 else 4 - in - length_aux s (c + 1) (i + k) - in - length_aux s 0 0 - -let utf8string s = - fancystring s (utf8_length s) - -let utf8format f = - Printf.ksprintf utf8string f - -let hardline = - HardLine - -let blank n = - match n with - | 0 -> - empty - | _ -> - Blank n - -let ifflat doc1 doc2 = - (* Avoid nesting [IfFlat] in the left-hand side of [IfFlat], as this - is redundant. *) - match doc1 with - | IfFlat (doc1, _) - | doc1 -> - IfFlat (doc1, doc2) - -let internal_break i = - ifflat (blank i) hardline - -let break0 = - internal_break 0 - -let break1 = - internal_break 1 - -let break i = - match i with - | 0 -> - break0 - | 1 -> - break1 - | _ -> - internal_break i - -let (^^) x y = - match x, y with - | Empty, _ -> - y - | _, Empty -> - x - | _, _ -> - Cat (requirement x ++ requirement y, x, y) - -let nest i x = - assert (i >= 0); - Nest (requirement x, i, x) - -let group x = - let req = requirement x in - (* Minor optimisation: an infinite requirement dissolves a group. *) - if req = infinity then - x - else - Group (req, x) - -let align x = - Align (requirement x, x) - -let range hook x = - Range (requirement x, hook, x) - -let custom c = - (* Sanity check. *) - assert (c#requirement >= 0); - Custom c - -(* ------------------------------------------------------------------------- *) - -(* This function expresses the following invariant: if we are in flattening - mode, then we must be within bounds, i.e. the width and ribbon width - constraints must be respected. *) - -let ok state flatten : bool = - not flatten || - state.column <= state.width && state.column <= state.last_indent + state.ribbon - -(* ------------------------------------------------------------------------- *) - -(* The pretty rendering engine. *) - -(* The renderer is supposed to behave exactly like Daan Leijen's, although its - implementation is quite radically different, and simpler. Our documents are - constructed eagerly, as opposed to lazily. This means that we pay a large - space overhead, but in return, we get the ability of computing information - bottom-up, as described above, which allows to render documents without - backtracking or buffering. *) - -(* The [state] record is never copied; it is just threaded through. In - addition to it, the parameters [indent] and [flatten] influence the - manner in which the document is rendered. *) - -(* The code is written in tail-recursive style, so as to avoid running out of - stack space if the document is very deep. Each [KCons] cell in a - continuation represents a pending call to [pretty]. Each [KRange] cell - represents a pending call to a user-provided range hook. *) - -type cont = - | KNil - | KCons of int * bool * document * cont - | KRange of (range -> unit) * point * cont - -let rec pretty - (output : output) - (state : state) - (indent : int) - (flatten : bool) - (doc : document) - (cont : cont) -: unit = - match doc with - - | Empty -> - continue output state cont - - | Char c -> - output#char c; - state.column <- state.column + 1; - (* assert (ok state flatten); *) - continue output state cont - - | String s -> - let len = String.length s in - output#substring s 0 len; - state.column <- state.column + len; - (* assert (ok state flatten); *) - continue output state cont - - | FancyString (s, ofs, len, apparent_length) -> - output#substring s ofs len; - state.column <- state.column + apparent_length; - (* assert (ok state flatten); *) - continue output state cont - - | Blank n -> - blanks output n; - state.column <- state.column + n; - (* assert (ok state flatten); *) - continue output state cont - - | HardLine -> - (* We cannot be in flattening mode, because a hard line has an [infinity] - requirement, and we attempt to render a group in flattening mode only - if this group's requirement is met. *) - assert (not flatten); - (* Emit a hardline. *) - output#char '\n'; - blanks output indent; - state.line <- state.line + 1; - state.column <- indent; - state.last_indent <- indent; - (* Continue. *) - continue output state cont - - | IfFlat (doc1, doc2) -> - (* Pick an appropriate sub-document, based on the current flattening - mode. *) - pretty output state indent flatten (if flatten then doc1 else doc2) cont - - | Cat (_, doc1, doc2) -> - (* Push the second document onto the continuation. *) - pretty output state indent flatten doc1 (KCons (indent, flatten, doc2, cont)) - - | Nest (_, j, doc) -> - pretty output state (indent + j) flatten doc cont - - | Group (req, doc) -> - (* If we already are in flattening mode, stay in flattening mode; we - are committed to it. If we are not already in flattening mode, we - have a choice of entering flattening mode. We enter this mode only - if we know that this group fits on this line without violating the - width or ribbon width constraints. Thus, we never backtrack. *) - let flatten = - flatten || - let column = state.column ++ req in - column <== state.width && column <== state.last_indent + state.ribbon - in - pretty output state indent flatten doc cont - - | Align (_, doc) -> - (* The effect of this combinator is to set [indent] to [state.column]. - Usually [indent] is equal to [state.last_indent], hence setting it - to [state.column] increases it. However, if [nest] has been used - since the current line began, then this could cause [indent] to - decrease. *) - (* assert (state.column > state.last_indent); *) - pretty output state state.column flatten doc cont - - | Range (_, hook, doc) -> - let start : point = (state.line, state.column) in - pretty output state indent flatten doc (KRange (hook, start, cont)) - - | Custom c -> - (* Invoke the document's custom rendering function. *) - c#pretty output state indent flatten; - (* Sanity check. *) - assert (ok state flatten); - (* Continue. *) - continue output state cont - -and continue output state = function - | KNil -> - () - | KCons (indent, flatten, doc, cont) -> - pretty output state indent flatten doc cont - | KRange (hook, start, cont) -> - let finish : point = (state.line, state.column) in - hook (start, finish); - continue output state cont - -(* Publish a version of [pretty] that does not take an explicit continuation. - This function may be used by authors of custom documents. We do not expose - the internal [pretty] -- the one that takes a continuation -- because we - wish to simplify the user's life. The price to pay is that calls that go - through a custom document cannot be tail calls. *) - -let pretty output state indent flatten doc = - pretty output state indent flatten doc KNil - -(* ------------------------------------------------------------------------- *) - -(* The compact rendering algorithm. *) - -let rec compact output doc cont = - match doc with - | Empty -> - continue output cont - | Char c -> - output#char c; - continue output cont - | String s -> - let len = String.length s in - output#substring s 0 len; - continue output cont - | FancyString (s, ofs, len, _apparent_length) -> - output#substring s ofs len; - continue output cont - | Blank n -> - blanks output n; - continue output cont - | HardLine -> - output#char '\n'; - continue output cont - | Cat (_, doc1, doc2) -> - compact output doc1 (doc2 :: cont) - | IfFlat (doc, _) - | Nest (_, _, doc) - | Group (_, doc) - | Align (_, doc) - | Range (_, _, doc) -> - compact output doc cont - | Custom c -> - (* Invoke the document's custom rendering function. *) - c#compact output; - continue output cont - -and continue output cont = - match cont with - | [] -> - () - | doc :: cont -> - compact output doc cont - -let compact output doc = - compact output doc [] - -(* ------------------------------------------------------------------------- *) - -(* We now instantiate the renderers for the three kinds of output channels. *) - -(* This is just boilerplate. *) - -module type RENDERER = sig - type channel - type document - val pretty: float -> int -> channel -> document -> unit - val compact: channel -> document -> unit -end - -module MakeRenderer (X : sig - type channel - val output: channel -> output -end) -: RENDERER with type channel = X.channel and type document = document -= struct - type channel = X.channel - type nonrec document = document - let pretty rfrac width channel doc = pretty (X.output channel) (initial rfrac width) 0 false doc - let compact channel doc = compact (X.output channel) doc -end - -module ToChannel = - MakeRenderer(struct - type channel = out_channel - let output channel = new buffering (new channel_output channel) - end) - -module ToBuffer = - MakeRenderer(struct - type channel = Buffer.t - let output buffer = new buffering (new buffer_output buffer) - end) - -module ToFormatter = - MakeRenderer(struct - type channel = Format.formatter - let output fmt = new buffering (new formatter_output fmt) - end) diff --git a/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.mli b/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.mli deleted file mode 100644 index 4d03b12..0000000 --- a/tests/menhir_tests/test10menhir_with_aux_args/PPrintEngine.mli +++ /dev/null @@ -1,307 +0,0 @@ -(******************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2022 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(******************************************************************************) - -(**[PPrint] is an OCaml library for {b pretty-printing textual documents}. - It takes care of {b indentation and line breaks}, and is typically used - to {b pretty-print code}. *) - -(** {1:building Building Documents} *) - -(**The abstract type of documents. *) -type document - -(** {2 Atomic Documents} *) - -(**[empty] is the empty document. *) -val empty: document - -(**[char c] is an atomic document that consists of the single character [c]. - This character must not be a newline character. *) -val char: char -> document - -(**[string s] is an atomic document that consists of the string [s]. This - string must not contain a newline. The printing engine assumes that the - ideal width of this string is [String.length s]. This assumption is safe - if this is an ASCII string. Otherwise, {!fancystring} or {!utf8string} - should be preferred. *) -val string: string -> document - -(**[substring s ofs len] is an atomic document that consists of the portion - of the string [s] delimited by the offset [ofs] and the length [len]. - This portion must not contain a newline. [substring s ofs len] is - equivalent to [string (String.sub s ofs len)], but is expected to be more - efficient, as the substring is not actually extracted. *) -val substring: string -> int -> int -> document - -(**[fancystring s alen] is an atomic document that consists of the string - [s]. This string must not contain a newline. The string may contain fancy - characters: color escape characters, UTF-8 characters, etc. Thus, its - apparent length (which measures how many columns the text will take up on - screen) differs from its length in bytes. The printing engine assumes - that its apparent length is [alen]. *) -val fancystring: string -> int -> document - -(**[fancysubstring s ofs len alen] is equivalent to [fancystring (String.sub - s ofs len) alen]. *) -val fancysubstring : string -> int -> int -> int -> document - -(**[utf8string s] is an atomic document that consists of the UTF-8-encoded - string [s]. This string must not contain a newline. [utf8string s] is - equivalent to [fancystring s (utf8_length s)], where [utf8_length s] is - the apparent length of the UTF-8-encoded string [s]. *) -val utf8string: string -> document - -(** [utf8format format ...] is equivalent to - [utf8string (Printf.sprintf format ...)]. *) -val utf8format: ('a, unit, string, document) format4 -> 'a - -(** {2 Blanks and Newlines} *) - -(**The atomic document [hardline] represents a forced newline. This document - has infinite ideal width: thus, if there is a choice between printing it - in flat mode and printing it in normal mode, normal mode is preferred. In - other words, when [hardline] is placed directly inside a group, this - group is dissolved: [group hardline] is equivalent to [hardline]. This - combinator should be seldom used; consider using {!break} instead. *) -val hardline: document - -(**The atomic document [blank n] consists of [n] blank characters. A blank - character is like an ordinary ASCII space character [char ' '], except - that blank characters that appear at the end of a line are automatically - suppressed. *) -val blank: int -> document - -(**[space] is a synonym for [blank 1]. It consists of one blank character. - It is therefore not equivalent to [char ' ']. *) -val space: document - -(**The document [break n] is a breakable blank of width [n]. It produces [n] - blank characters if the printing engine is in flat mode, and a single - newline character if the printing engine is in normal mode. [break 1] is - equivalent to [ifflat (blank 1) hardline]. *) -val break: int -> document - -(** {2 Composite Documents} *) - -(**[doc1 ^^ doc2] is the concatenation of the documents [doc1] and [doc2]. *) -val (^^): document -> document -> document - -(**[group doc] encodes a choice. If the document [doc] fits on the current - line, then it is rendered on a single line, in flat mode. (All [group] - combinators inside it are then ignored.) Otherwise, this group is - dissolved, and [doc] is rendered in normal mode. There might be more - groups within [doc], whose presence leads to further choices being - explored. *) -val group: document -> document - -(**[ifflat doc1 doc2] is rendered as [doc1] if the printing engine is in - flat mode, that is, if the printing engine has determined that some - enclosing group fits on the current line. Otherwise, it is rendered as - [doc2]. Use this combinator with caution! Because the printing engine is - free to choose between [doc1] and [doc2], these documents must be - semantically equivalent. It is up to the user to enforce this property. *) -val ifflat: document -> document -> document - -(**To render the document [nest j doc], the printing engine temporarily - increases the current indentation level by [j], then renders [doc]. The - effect of the current indentation level is as follows: every time a - newline character is emitted, it is immediately followed by [n] blank - characters, where [n] is the current indentation level. Thus, one may - think of [nest j doc] roughly as the document [doc] in which [j] blank - characters have been inserted after every newline character. *) -val nest: int -> document -> document - -(**To render [align doc], the printing engine sets the current indentation - level to the current column, then renders [doc]. In other words, the - document [doc] is rendered within a box whose upper left corner is the - current position of the printing engine. *) -val align: document -> document - -(**A point is a pair of a line number and a column number. *) -type point = - int * int - -(**A range is a pair of points. *) -type range = - point * point - -(**The document [range hook doc] is printed like the document [doc], but - allows the caller to register a hook that is applied, when the document - is printed, to the range occupied by this document in the output text. - This offers a way of mapping positions in the output text back to - (sub)documents. *) -val range: (range -> unit) -> document -> document - -(** {1:rendering Rendering Documents} *) - -(**Three renderers are available. They offer the same API, described - by the signature {!RENDERER}, and differ only in the nature of the - output channel that they use. *) - -(**This signature describes the document renderers in a manner that - is independent of the type of the output channel. *) -module type RENDERER = sig - - (**The type of the output channel. *) - type channel - - (**The type of documents. *) - type document - - (** [pretty rfrac width channel document] pretty-prints the document - [document] into the output channel [channel]. The parameter [width] is - the maximum number of characters per line. The parameter [rfrac] is the - ribbon width, a fraction relative to [width]. The ribbon width is the - maximum number of non-indentation characters per line. *) - val pretty: float -> int -> channel -> document -> unit - - (** [compact channel document] prints the document [document] to the output - channel [channel]. No indentation is used. All newline instructions are - respected, that is, no groups are flattened. *) - val compact: channel -> document -> unit - -end - -(**This renderer sends its output into an output channel. *) -module ToChannel : RENDERER - with type channel = out_channel - and type document = document - -(**This renderer sends its output into a memory buffer. *) -module ToBuffer : RENDERER - with type channel = Buffer.t - and type document = document - -(**This renderer sends its output into a formatter channel. *) -module ToFormatter : RENDERER - with type channel = Format.formatter - and type document = document - -(** {1:defining Defining Custom Documents} *) - -(**It is possible to define custom document constructors, provided they meet - the expectations of the printing engine. In short, the custom document - combinator {!val-custom} expects an object of class {!class-type-custom}. - This object must provide three methods. The method [requirement] must - compute the ideal width of the custom document. The methods [pretty] and - [compact] must render the custom document. For this purpose, they have - access to the {{!output}output channel} and to the {{!state}state} of the - printing engine. *) - -(** A width requirement is expressed as an integer. The value [max_int] - is reserved and represents infinity. *) -type requirement = int - -(**[infinity] represents an infinite width requirement. *) -val infinity : requirement - -(**An output channel is abstractly represented as an object equipped with - methods for displaying one character and for displaying a substring. *) -class type output = object - - (**[char c] sends the character [c] to the output channel. *) - method char: char -> unit - - (**[substring s ofs len] sends the substring of [s] delimited by the - offset [ofs] and the length [len] to the output channel. *) - method substring: string -> int (* offset *) -> int (* length *) -> unit - -end - -(**The internal state of the rendering engine is exposed to the user who - wishes to define custom documents. However, its structure is subject to - change in future versions of the library. *) -type state = { - - width: int; - (** The line width. This parameter is fixed throughout the execution of - the renderer. *) - - ribbon: int; - (** The ribbon width. This parameter is fixed throughout the execution of - the renderer. *) - - mutable last_indent: int; - (** The number of blanks that were printed at the beginning of the current - line. This field is updated (only) when a hardline is emitted. It is - used (only) to determine whether the ribbon width constraint is - respected. *) - - mutable line: int; - (** The current line. This field is updated (only) when a hardline is - emitted. It is not used by the pretty-printing engine itself. *) - - mutable column: int; - (** The current column. This field must be updated whenever something is - sent to the output channel. It is used (only) to determine whether the - width constraint is respected. *) - - } - -(**A custom document is defined by implementing an object of class - {!class-type-custom}. *) -class type custom = object - - (**A custom document must publish the width (i.e., the number of columns) - that it would like to occupy if printed on a single line (in flat - mode). The special value [infinity] means that this document cannot be - printed on a single line; this value causes any groups that contain - this document to be dissolved. This method should in principle work in - constant time. *) - method requirement: requirement - - (**The method [pretty] is used by the main rendering algorithm. It has - access to the output channel and to the printing engine's internal - state. In addition, it receives the current indentation level and a - Boolean flag that tells whether the engine is currently in flat mode. - If the engine is in flat mode, then the document must be printed on a - single line, in a manner that is consistent with the width requirement - that was published ahead of time. If the engine is in normal mode, then - there is no such obligation. The state must be updated in a manner that - is consistent with what is sent to the output channel. *) - method pretty: output -> state -> int -> bool -> unit - - (**The method [compact] is used by the compact rendering algorithm. It - has access to the output channel only. *) - method compact: output -> unit - -end - -(**[custom] constructs a custom document out an object of type - {!class-type-custom}. *) -val custom: custom -> document - -(**Some of the key functions of the library are exposed, in the hope that - they may be useful to authors of custom (leaf and composite) documents. - In the case of a leaf document, they can help perform certain basic - functions; for instance, applying the function {!pretty} to the document - {!hardline} is a simple way of printing a hardline, while respecting the - indentation parameters and updating the state in a correct manner. - Similarly, applying {!pretty} to the document [blank n] is a simple way - of printing [n] blank characters. In the case of a composite document - (one that contains subdocuments), these functions are essential: they - allow computing the width requirement of a subdocument and displaying a - subdocument. *) - -(**[requirement doc] computes the width requirement of the document [doc]. - It runs in constant time. *) -val requirement: document -> requirement - -(**[pretty output state indent flatten doc] prints the document [doc]. See - the documentation of the method [pretty] in the class - {!class-type-custom}. *) -val pretty: output -> state -> int -> bool -> document -> unit - -(**[compact output doc] prints the document [doc]. See the documentation of - the method [compact] in the class {!class-type-custom}. *) -val compact: output -> document -> unit diff --git a/tests/menhir_tests/test10menhir_with_aux_args/pprint-master.zip b/tests/menhir_tests/test10menhir_with_aux_args/pprint-master.zip deleted file mode 100644 index a6e0ac3e6613c585f739adaac12f53ab36d8f9a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51643 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¡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%0AGH%=XRPiP`|<5CdFw>T5oVrv300B6ksdO3A6xq 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 diff --git a/tests/menhir_tests/test10menhir_with_aux_rules/Makefile b/tests/menhir_tests/test10menhir_with_aux_rules/Makefile index cf5e551..b013a56 100644 --- a/tests/menhir_tests/test10menhir_with_aux_rules/Makefile +++ b/tests/menhir_tests/test10menhir_with_aux_rules/Makefile @@ -8,7 +8,7 @@ MENHIR := menhir MENHIRFLAGS := --infer -OCAMLBUILD := ocamlbuild -use-ocamlfind -use-menhir -menhir "$(MENHIR) $(MENHIRFLAGS)" +OCAMLBUILD := ocamlbuild -use-ocamlfind -use-menhir -menhir "$(MENHIR) $(MENHIRFLAGS)" -package pprint MAIN := main diff --git a/tests/menhir_tests/test10menhir_with_aux_rules/PPrint.ml b/tests/menhir_tests/test10menhir_with_aux_rules/PPrint.ml deleted file mode 100644 index da55896..0000000 --- a/tests/menhir_tests/test10menhir_with_aux_rules/PPrint.ml +++ /dev/null @@ -1,468 +0,0 @@ -(******************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2022 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(******************************************************************************) - -include PPrintEngine - -(* -------------------------------------------------------------------------- *) - -(* Predefined single-character documents. *) - -let lparen = char '(' -let rparen = char ')' -let langle = char '<' -let rangle = char '>' -let lbrace = char '{' -let rbrace = char '}' -let lbracket = char '[' -let rbracket = char ']' -let squote = char '\'' -let dquote = char '"' -let bquote = char '`' -let semi = char ';' -let colon = char ':' -let comma = char ',' -let dot = char '.' -let sharp = char '#' -let slash = char '/' -let backslash = char '\\' -let equals = char '=' -let qmark = char '?' -let tilde = char '~' -let at = char '@' -let percent = char '%' -let dollar = char '$' -let caret = char '^' -let ampersand = char '&' -let star = char '*' -let plus = char '+' -let minus = char '-' -let underscore = char '_' -let bang = char '!' -let bar = char '|' - -(* -------------------------------------------------------------------------- *) - -(* Repetition. *) - -let twice doc = - doc ^^ doc - -let repeat n doc = - let rec loop n doc accu = - if n = 0 then - accu - else - loop (n - 1) doc (doc ^^ accu) - in - loop n doc empty - -(* -------------------------------------------------------------------------- *) - -(* Delimiters. *) - -let precede l x = l ^^ x -let terminate r x = x ^^ r -let enclose l r x = l ^^ x ^^ r - -let squotes = enclose squote squote -let dquotes = enclose dquote dquote -let bquotes = enclose bquote bquote -let braces = enclose lbrace rbrace -let parens = enclose lparen rparen -let angles = enclose langle rangle -let brackets = enclose lbracket rbracket - -(* -------------------------------------------------------------------------- *) - -(* Some functions on lists. *) - -(* A variant of [fold_left] that keeps track of the element index. *) - -let foldli (f : int -> 'b -> 'a -> 'b) (accu : 'b) (xs : 'a list) : 'b = - let r = ref 0 in - List.fold_left (fun accu x -> - let i = !r in - r := i + 1; - f i accu x - ) accu xs - -(* -------------------------------------------------------------------------- *) - -(* Working with lists of documents. *) - -let concat docs = - (* We take advantage of the fact that [^^] operates in constant - time, regardless of the size of its arguments. The document - that is constructed is essentially a reversed list (i.e., a - tree that is biased towards the left). This is not a problem; - when pretty-printing this document, the engine will descend - along the left branch, pushing the nodes onto its stack as - it goes down, effectively reversing the list again. *) - List.fold_left (^^) empty docs - -let separate sep docs = - foldli (fun i accu doc -> - if i = 0 then - doc - else - accu ^^ sep ^^ doc - ) empty docs - -let concat_map f xs = - List.fold_left (fun accu x -> - accu ^^ f x - ) empty xs - -let separate_map sep f xs = - foldli (fun i accu x -> - if i = 0 then - f x - else - accu ^^ sep ^^ f x - ) empty xs - -let separate2 sep last_sep docs = - let n = List.length docs in - foldli (fun i accu doc -> - if i = 0 then - doc - else - accu ^^ (if i < n - 1 then sep else last_sep) ^^ doc - ) empty docs - -let optional f = function - | None -> - empty - | Some x -> - f x - -(* -------------------------------------------------------------------------- *) - -(* Text. *) - -(* This variant of [String.index_from] returns an option. *) - -let index_from s i c = - try - Some (String.index_from s i c) - with Not_found -> - None - -(* [lines s] chops the string [s] into a list of lines, which are turned - into documents. *) - -let lines s = - let rec chop accu i = - match index_from s i '\n' with - | Some j -> - let accu = substring s i (j - i) :: accu in - chop accu (j + 1) - | None -> - substring s i (String.length s - i) :: accu - in - List.rev (chop [] 0) - -let arbitrary_string s = - separate (break 1) (lines s) - -(* [split ok s] splits the string [s] at every occurrence of a character - that satisfies the predicate [ok]. The substrings thus obtained are - turned into documents, and a list of documents is returned. No information - is lost: the concatenation of the documents yields the original string. - This code is not UTF-8 aware. *) - -let split ok s = - let n = String.length s in - let rec index_from i = - if i = n then - None - else if ok s.[i] then - Some i - else - index_from (i + 1) - in - let rec chop accu i = - match index_from i with - | Some j -> - let accu = substring s i (j - i) :: accu in - let accu = char s.[j] :: accu in - chop accu (j + 1) - | None -> - substring s i (String.length s - i) :: accu - in - List.rev (chop [] 0) - -(* [words s] chops the string [s] into a list of words, which are turned - into documents. *) - -let words s = - let n = String.length s in - (* A two-state finite automaton. *) - (* In this state, we have skipped at least one blank character. *) - let rec skipping accu i = - if i = n then - (* There was whitespace at the end. Drop it. *) - accu - else match s.[i] with - | ' ' - | '\t' - | '\n' - | '\r' -> - (* Skip more whitespace. *) - skipping accu (i + 1) - | _ -> - (* Begin a new word. *) - word accu i (i + 1) - (* In this state, we have skipped at least one non-blank character. *) - and word accu i j = - if j = n then - (* Final word. *) - substring s i (j - i) :: accu - else match s.[j] with - | ' ' - | '\t' - | '\n' - | '\r' -> - (* A new word has been identified. *) - let accu = substring s i (j - i) :: accu in - skipping accu (j + 1) - | _ -> - (* Continue inside the current word. *) - word accu i (j + 1) - in - List.rev (skipping [] 0) - -let flow_map sep f docs = - foldli (fun i accu doc -> - if i = 0 then - f doc - else - accu ^^ - (* This idiom allows beginning a new line if [doc] does not - fit on the current line. *) - group (sep ^^ f doc) - ) empty docs - -let flow sep docs = - flow_map sep (fun x -> x) docs - -let url s = - flow (break 0) (split (function '/' | '.' -> true | _ -> false) s) - -(* -------------------------------------------------------------------------- *) -(* Alignment and indentation. *) - -let hang i d = - align (nest i d) - -let ( !^ ) = string - -let ( ^/^ ) x y = - x ^^ break 1 ^^ y - -let prefix n b x y = - group (x ^^ nest n (break b ^^ y)) - -let (^//^) = - prefix 2 1 - -let jump n b y = - group (nest n (break b ^^ y)) - -let infix n b op x y = - prefix n b (x ^^ blank b ^^ op) y - -let surround n b opening contents closing = - group (opening ^^ nest n ( break b ^^ contents) ^^ break b ^^ closing ) - -let soft_surround n b opening contents closing = - group (opening ^^ nest n (group (break b) ^^ contents) ^^ group (break b ^^ closing)) - -let surround_separate n b void opening sep closing docs = - match docs with - | [] -> - void - | _ :: _ -> - surround n b opening (separate sep docs) closing - -let surround_separate_map n b void opening sep closing f xs = - match xs with - | [] -> - void - | _ :: _ -> - surround n b opening (separate_map sep f xs) closing - -(* -------------------------------------------------------------------------- *) -(* Printing OCaml values. *) - -module OCaml = struct - -open Printf - -type constructor = string -type type_name = string -type record_field = string -type tag = int - -(* -------------------------------------------------------------------------- *) - -(* This internal [sprintf]-like function produces a document. We use [string], - as opposed to [arbitrary_string], because the strings that we produce will - never contain a newline character. *) - -let dsprintf format = - ksprintf string format - -(* -------------------------------------------------------------------------- *) - -(* Nicolas prefers using this code as opposed to just [sprintf "%g"] or - [sprintf "%f"]. The latter print [inf] and [-inf], whereas OCaml - understands [infinity] and [neg_infinity]. [sprintf "%g"] does not add a - trailing dot when the number happens to be an integral number. [sprintf - "%F"] seems to lose precision and ignores the precision modifier. *) - -let valid_float_lexeme (s : string) : string = - let l = String.length s in - let rec loop i = - if i >= l then - (* If we reach the end of the string and have found only characters in - the set '0' .. '9' and '-', then this string will be considered as an - integer literal by OCaml. Adding a trailing dot makes it a float - literal. *) - s ^ "." - else - match s.[i] with - | '0' .. '9' | '-' -> loop (i + 1) - | _ -> s - in loop 0 - -(* This function constructs a string representation of a floating point - number. This representation is supposed to be accepted by OCaml as a - valid floating point literal. *) - -let float_representation (f : float) : string = - match classify_float f with - | FP_nan -> - "nan" - | FP_infinite -> - if f < 0.0 then "neg_infinity" else "infinity" - | _ -> - (* Try increasing precisions and validate. *) - let s = sprintf "%.12g" f in - if f = float_of_string s then valid_float_lexeme s else - let s = sprintf "%.15g" f in - if f = float_of_string s then valid_float_lexeme s else - sprintf "%.18g" f - -(* -------------------------------------------------------------------------- *) - -(* A few constants and combinators, used below. *) - -let some = - string "Some" - -let none = - string "None" - -let lbracketbar = - string "[|" - -let rbracketbar = - string "|]" - -let seq1 opening separator closing = - surround_separate 2 0 - (opening ^^ closing) opening (separator ^^ break 1) closing - -let seq2 opening separator closing = - surround_separate_map 2 1 - (opening ^^ closing) opening (separator ^^ break 1) closing - -(* -------------------------------------------------------------------------- *) - -(* The following functions are printers for many types of OCaml values. *) - -(* There is no protection against cyclic values. *) - -let tuple = - seq1 lparen comma rparen - -let variant _ cons _ args = - match args with - | [] -> - !^cons - | _ :: _ -> - !^cons ^^ tuple args - -let record _ fields = - seq2 lbrace semi rbrace (fun (k, v) -> infix 2 1 equals !^k v) fields - -let option f = function - | None -> - none - | Some x -> - some ^^ tuple [f x] - -let list f xs = - seq2 lbracket semi rbracket f xs - -let flowing_list f xs = - group (lbracket ^^ space ^^ nest 2 ( - flow_map (semi ^^ break 1) f xs - ) ^^ space ^^ rbracket) - -let array f xs = - seq2 lbracketbar semi rbracketbar f (Array.to_list xs) - -let flowing_array f xs = - group (lbracketbar ^^ space ^^ nest 2 ( - flow_map (semi ^^ break 1) f (Array.to_list xs) - ) ^^ space ^^ rbracketbar) - -let ref f x = - record "ref" ["contents", f !x] - -let float f = - string (float_representation f) - -let int = - dsprintf "%d" - -let int32 = - dsprintf "%ld" - -let int64 = - dsprintf "%Ld" - -let nativeint = - dsprintf "%nd" - -let char = - dsprintf "%C" - -let bool = - dsprintf "%B" - -let unit = - dsprintf "()" - -let string = - dsprintf "%S" - -let unknown tyname _ = - dsprintf "" tyname - -type representation = - document - -end (* OCaml *) diff --git a/tests/menhir_tests/test10menhir_with_aux_rules/PPrint.mli b/tests/menhir_tests/test10menhir_with_aux_rules/PPrint.mli deleted file mode 100644 index 89eeeeb..0000000 --- a/tests/menhir_tests/test10menhir_with_aux_rules/PPrint.mli +++ /dev/null @@ -1,345 +0,0 @@ -(******************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2022 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(******************************************************************************) - -include module type of PPrintEngine (** @inline *) - -(** {1:combinators High-Level Combinators} *) - -(** {2 Single Characters} *) - -(**The following atomic documents consist of a single character. Each of - them is a synonym for the application of {!char} to some constant - character. For instance, {!lparen} is a synonym for [char '(']. *) - -val lparen: document -val rparen: document -val langle: document -val rangle: document -val lbrace: document -val rbrace: document -val lbracket: document -val rbracket: document -val squote: document -val dquote: document -val bquote: document -val semi: document -val colon: document -val comma: document -val dot: document -val sharp: document -val slash: document -val backslash: document -val equals: document -val qmark: document -val tilde: document -val at: document -val percent: document -val dollar: document -val caret: document -val ampersand: document -val star: document -val plus: document -val minus: document -val underscore: document -val bang: document -val bar: document - -(** {2 Delimiters} *) - -(**[precede l x] is [l ^^ x]. *) -val precede: document -> document -> document - -(**[terminate r x] is [x ^^ r]. *) -val terminate: document -> document -> document - -(**[enclose l r x] is [l ^^ x ^^ r]. *) -val enclose: document -> document -> document -> document - -(**The following combinators enclose a document within a pair of delimiters. - They are partial applications of [enclose]. No whitespace or line break - is introduced. *) - -val squotes: document -> document -val dquotes: document -> document -val bquotes: document -> document -val braces: document -> document -val parens: document -> document -val angles: document -> document -val brackets: document -> document - -(** {2 Repetition} *) - -(**[twice doc] is the document obtained by concatenating two copies of - the document [doc]. *) -val twice: document -> document - -(**[repeat n doc] is the document obtained by concatenating [n] copies of - the document [doc]. *) -val repeat: int -> document -> document - -(** {2 Lists and Options} *) - -(**[concat docs] is the concatenation of the documents in the list [docs]. *) -val concat: document list -> document - -(**[separate sep docs] is the concatenation of the documents in the list - [docs]. The separator [sep] is inserted between every two adjacent - documents. *) -val separate: document -> document list -> document - -(**[concat_map f xs] is equivalent to [concat (List.map f xs)]. *) -val concat_map: ('a -> document) -> 'a list -> document - -(**[separate_map sep f xs] is equivalent to [separate sep (List.map f xs)]. *) -val separate_map: document -> ('a -> document) -> 'a list -> document - -(**[separate2 sep last_sep docs] is the concatenation of the documents in - the list [docs]. The separator [sep] is inserted between every two - adjacent documents, except between the last two documents, where the - separator [last_sep] is used instead. *) -val separate2: document -> document -> document list -> document - -(**[optional f None] is the empty document. [optional f (Some x)] is - the document [f x]. *) -val optional: ('a -> document) -> 'a option -> document - -(** {2 Text} *) - -(**[lines s] is the list of documents obtained by splitting [s] at newline - characters, and turning each line into a document via [substring]. This - code is not UTF-8 aware. *) -val lines: string -> document list - -(**[arbitrary_string s] is equivalent to [separate (break 1) (lines s)]. - It is analogous to [string s], but is valid even if the string [s] - contains newline characters. *) -val arbitrary_string: string -> document - -(**[words s] is the list of documents obtained by splitting [s] at whitespace - characters, and turning each word into a document via [substring]. All - whitespace is discarded. This code is not UTF-8 aware. *) -val words: string -> document list - -(**[split ok s] splits the string [s] before and after every occurrence of a - character that satisfies the predicate [ok]. The substrings thus obtained - are turned into documents, and a list of documents is returned. No - information is lost: the concatenation of the documents yields the - original string. This code is not UTF-8 aware. *) -val split: (char -> bool) -> string -> document list - -(**[flow sep docs] separates the documents in the list [docs] with the - separator [sep] and arranges for a new line to begin whenever a document - does not fit on the current line. This is useful for typesetting - free-flowing, ragged-right text. A typical choice of [sep] is [break b], - where [b] is the number of spaces that must be inserted between two - consecutive words (when displayed on the same line). *) -val flow: document -> document list -> document - -(**[flow_map sep f docs] is equivalent to [flow sep (List.map f docs)]. *) -val flow_map: document -> ('a -> document) -> 'a list -> document - -(**[url s] is a possible way of displaying the URL [s]. A potential line - break is inserted immediately before and immediately after every slash - and dot character. *) -val url: string -> document - -(** {2 Alignment and Indentation} *) - -(**[hang n doc] is analogous to [align], but additionally indents all lines, - except the first one, by [n]. Thus, the text in the box forms a hanging - indent. *) -val hang: int -> document -> document - -(**[prefix n b left right] has the following flat layout: - {[ - left right - ]} - and the following non-flat layout: - {[ - left - right - ]} - The parameter [n] controls the nesting of [right] (when not flat). - The parameter [b] controls the number of spaces between [left] and [right] - (when flat). *) -val prefix: int -> int -> document -> document -> document - -(**[jump n b right] is equivalent to [prefix n b empty right]. *) -val jump: int -> int -> document -> document - -(**[infix n b middle left right] has the following flat layout: - {[ - left middle right - ]} - and the following non-flat layout: - {[ - left middle - right - ]} - The parameter [n] controls the nesting of [right] (when not flat). - The parameter [b] controls the number of spaces between [left] and [middle] - (always) and between [middle] and [right] (when flat). *) -val infix: int -> int -> document -> document -> document -> document - -(**[surround n b opening contents closing] has the following flat layout: - {[ - opening contents closing - ]} - and the following non-flat layout: - {[ - opening - contents - closing - ]} - The parameter [n] controls the nesting of [contents] (when not flat). - The parameter [b] controls the number of spaces between [opening] and - [contents] and between [contents] and [closing] (when flat). -*) -val surround: int -> int -> document -> document -> document -> document - -(**[soft_surround] is analogous to [surround], but involves more than one - group, so it offers possibilities other than the completely flat layout - (where [opening], [contents], and [closing] appear on a single line) and - the completely developed layout (where [opening], [contents], and - [closing] appear on separate lines). It tries to place the beginning of - [contents] on the same line as [opening], and to place [closing] on the - same line as the end of [contents], if possible. *) -val soft_surround: int -> int -> document -> document -> document -> document - -(**[surround_separate n b void opening sep closing docs] is equivalent to - [surround n b opening (separate sep docs) closing], except when the list - [docs] is empty, in which case it reduces to [void]. *) -val surround_separate: - int -> int -> - document -> document -> document -> document -> - document list -> document - -(**[surround_separate_map n b void opening sep closing f xs] is equivalent - to [surround_separate n b void opening sep closing (List.map f xs)]. *) -val surround_separate_map: - int -> int -> - document -> document -> document -> document -> - ('a -> document) -> 'a list -> document - -(** {2 Short-Hands} *) - -(**[!^s] is a short-hand for [string s]. *) -val ( !^ ) : string -> document - -(**[x ^/^ y] separates [x] and [y] with a breakable space. - It is a short-hand for [x ^^ break 1 ^^ y]. *) -val ( ^/^ ) : document -> document -> document - -(**[x ^//^ y] is a short-hand for [prefix 2 1 x y]. *) -val ( ^//^ ) : document -> document -> document - -(** {1:ocaml Printing OCaml Values} *) - -(**This module offers document combinators that help print OCaml values. The - strings produced by rendering these documents are supposed to be accepted - by the OCaml parser as valid values. - - These functions do {i not} distinguish between mutable and immutable - values. They do {i not} recognize sharing, and do {i not} incorporate a - protection against cyclic values. *) -module OCaml : sig - -(* The signature of this module is compatible with that expected by the - [camlp4] generator [Camlp4RepresentationGenerator]. This explains why - some functions have unused parameters. This is also the reason why - there is a type [representation]. *) - -type constructor = string -type type_name = string -type record_field = string -type tag = int - -(**[variant _ dc _ args] represents a constructed value whose data - constructor is [dc] and whose arguments are [args]. The other two - parameters are presently unused. *) -val variant : type_name -> constructor -> tag -> document list -> document - -(**[record _ fields] represents a record value whose fields are [fields]. - The other parameter is presently unused. *) -val record : type_name -> (record_field * document) list -> document - -(**[tuple args] represents a tuple value whose components are [args]. *) -val tuple : document list -> document - -(**[string s] represents the literal string [s]. *) -val string : string -> document - -(**[int i] represents the literal integer [i]. *) -val int : int -> document - -(**[int32 i] represents the literal 32-bit integer [i]. *) -val int32 : int32 -> document - -(**[int64 i] represents the literal 64-bit integer [i]. *) -val int64 : int64 -> document - -(**[nativeint i] represents the literal native integer [i]. *) -val nativeint : nativeint -> document - -(**[float f] represents the literal floating-point number [f]. *) -val float : float -> document - -(**[char c] represents the literal character [c]. *) -val char : char -> document - -(**[bool b] represents the Boolean value [b]. *) -val bool : bool -> document - -(**[unit] represents the unit constant [()]. *) -val unit : document - -(**[option f o] represents the option [o]. The representation of the - element, if present, is computed by the function [f]. *) -val option : ('a -> document) -> 'a option -> document - -(**[list f xs] represents the list [xs]. The representation of each element - is computed by the function [f]. If the whole list fits on a single line, - then it is printed on a single line; otherwise each element is printed on - a separate line. *) -val list : ('a -> document) -> 'a list -> document - -(**[flowing_list f xs] represents the list [xs]. The representation of each - element is computed by the function [f]. As many elements are possible - are printed on each line. *) -val flowing_list : ('a -> document) -> 'a list -> document - -(**[array f xs] represents the array [xs]. The representation of each - element is computed by the function [f]. If the whole array fits on a - single line, then it is printed on a single line; otherwise each element - is printed on a separate line. *) -val array : ('a -> document) -> 'a array -> document - -(**[flowing_array f xs] represents the array [xs]. The representation of - each element is computed by the function [f]. As many elements are - possible are printed on each line. *) -val flowing_array : ('a -> document) -> 'a array -> document - -(**[ref r] represents the reference [r]. The representation of the content - is computed by the function [f]. *) -val ref : ('a -> document) -> 'a ref -> document - -(** [unknown t _] represents an unknown value of type [t]. It is rendered - as a string of the form []. *) -val unknown : type_name -> 'a -> document - -(**/**) - -type representation = - document - -end (* OCaml *) diff --git a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.ml b/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.ml deleted file mode 100644 index 3131893..0000000 --- a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.ml +++ /dev/null @@ -1,751 +0,0 @@ -(******************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2022 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(******************************************************************************) - -(** A point is a pair of a line number and a column number. *) -type point = - int * int - -(** A range is a pair of points. *) -type range = - point * point - -(* ------------------------------------------------------------------------- *) - -(* A type of integers with infinity. *) - -type requirement = - int (* with infinity *) - -(* Infinity is encoded as [max_int]. *) - -let infinity : requirement = - max_int - -(* Addition of integers with infinity. *) - -let (++) (x : requirement) (y : requirement) : requirement = - if x = infinity || y = infinity then - infinity - else - x + y - -(* Comparison between an integer with infinity and a normal integer. *) - -let (<==) (x : requirement) (y : int) = - x <= y - -(* ------------------------------------------------------------------------- *) - -(* A uniform interface for output channels. *) - -class type output = object - - (** [char c] sends the character [c] to the output channel. *) - method char: char -> unit - - (** [substring s ofs len] sends the substring of [s] delimited by the - offset [ofs] and the length [len] to the output channel. *) - method substring: string -> int (* offset *) -> int (* length *) -> unit - -end - -(* ------------------------------------------------------------------------- *) - -(* Printing blank space. This is used both internally (to emit indentation - characters) and via the public combinator [blank]. *) - -let blank_length = - 80 - -let blank_buffer = - String.make blank_length ' ' - -let rec blanks (output : output) n = - if n <= 0 then - () - else if n <= blank_length then - output#substring blank_buffer 0 n - else begin - output#substring blank_buffer 0 blank_length; - blanks output (n - blank_length) - end - -(* ------------------------------------------------------------------------- *) - -(* The class [buffering] implements a wrapper that delays the printing of - blank characters. This includes indentation characters and characters - produced by the combinator [blank]. The printing of these characters is - delayed until it is known that they are followed by something on the same - line; if they are not followed with anything, then it is canceled. - - The actual printing task is delegated to the object [delegate], whose type - is [output]; the new object has type [output] as well. *) - -class buffering (delegate : output) : output = object (self) - - (* The number of blank characters that are withholding. *) - val mutable buffered = 0 - - (* [flush] sends out the blank characters that have been withheld. *) - method private flush = - blanks delegate buffered; - buffered <- 0 - - method char c : unit = - begin match c with - | '\n' -> - (* The current line ends here. Any blank characters that were withheld - are destroyed. This is where we avoid printing blank characters if - nothing follows them. *) - buffered <- 0 - | _ -> - (* The current line is nonempty. Any blank characters that were - withheld can now be flushed. *) - self#flush - end; - (* Print this character as usual. *) - delegate#char c - - method substring s pos len = - (* If this is a string of length zero, then there is nothing to do. *) - if len = 0 then - () - (* If this is a blank string (which we recognize by its address), then - its content is withheld. *) - else if s == blank_buffer then - buffered <- buffered + len - (* If this is not a blank string, then the blank characters that were - withheld up to this point can now be flushed. *) - else begin - self#flush; - delegate#substring s pos len - end - -end - -(* ------------------------------------------------------------------------- *) - -(* Three kinds of output channels are wrapped so as to satisfy the above - interface: OCaml output channels, OCaml memory buffers, and OCaml - formatters. *) - -class channel_output channel = object - method char = output_char channel - method substring = output_substring channel - (* We used to use [output], but, as of OCaml 4.02 and with -safe-string - enabled, the type of [output] has changed: this function now expects - an argument of type [bytes]. The new function [output_substring] must - be used instead. Furthermore, as of OCaml 4.06, -safe-string is enabled - by default. In summary, we require OCaml 4.02, use [output_substring], - and enable -safe-string. *) -end - -class buffer_output buffer = object - method char = Buffer.add_char buffer - method substring = Buffer.add_substring buffer -end - -class formatter_output fmt = object - method char = function - | '\n' -> Format.pp_force_newline fmt () - | ' ' -> Format.pp_print_space fmt () - | c -> Format.pp_print_char fmt c - - method substring str ofs len = - Format.pp_print_text fmt ( - if ofs = 0 && len = String.length str - then str - else String.sub str ofs len - ) -end - -(* ------------------------------------------------------------------------- *) - -(** The rendering engine maintains the following internal state. Its structure - is subject to change in future versions of the library. Nevertheless, it is - exposed to the user who wishes to define custom documents. *) - -type state = { - - width: int; - (** The line width. This parameter is fixed throughout the execution of - the renderer. *) - - ribbon: int; - (** The ribbon width. This parameter is fixed throughout the execution of - the renderer. *) - - mutable last_indent: int; - (** The number of blanks that were printed at the beginning of the current - line. This field is updated (only) when a hardline is emitted. It is - used (only) to determine whether the ribbon width constraint is - respected. *) - - mutable line: int; - (** The current line. This field is updated (only) when a hardline is - emitted. It is not used by the pretty-printing engine itself. *) - - mutable column: int; - (** The current column. This field must be updated whenever something is - sent to the output channel. It is used (only) to determine whether the - width constraint is respected. *) - - } - -(* ------------------------------------------------------------------------- *) - -(* [initial rfrac width] creates a fresh initial state. *) - -let initial rfrac width = { - width = width; - ribbon = max 0 (min width (truncate (float_of_int width *. rfrac))); - last_indent = 0; - line = 0; - column = 0 -} - -(* ------------------------------------------------------------------------- *) - -(** A custom document is defined by implementing the following methods. *) - -class type custom = object - - (** A custom document must publish the width (i.e., the number of columns) - that it would like to occupy if it is printed on a single line (that is, - in flattening mode). The special value [infinity] means that this - document cannot be printed on a single line; this value causes any - groups that contain this document to be dissolved. This method should - in principle work in constant time. *) - method requirement: requirement - - (** The method [pretty] is used by the main rendering algorithm. It has - access to the output channel and to the algorithm's internal state, as - described above. In addition, it receives the current indentation level - and the current flattening mode (on or off). If flattening mode is on, - then the document must be printed on a single line, in a manner that is - consistent with the requirement that was published ahead of time. If - flattening mode is off, then there is no such obligation. The state must - be updated in a manner that is consistent with what is sent to the - output channel. *) - method pretty: output -> state -> int -> bool -> unit - - (** The method [compact] is used by the compact rendering algorithm. It has - access to the output channel only. *) - method compact: output -> unit - -end - -(* ------------------------------------------------------------------------- *) - -(* Here is the algebraic data type of documents. It is analogous to Daan - Leijen's version, but the binary constructor [Union] is replaced with - the unary constructor [Group], and the constant [Line] is replaced with - more general constructions, namely [IfFlat], which provides alternative - forms depending on the current flattening mode, and [HardLine], which - represents a newline character, and causes a failure in flattening mode. *) - -type document = - - (* [Empty] is the empty document. *) - - | Empty - - (* [Char c] is a document that consists of the single character [c]. We - enforce the invariant that [c] is not a newline character. *) - - | Char of char - - (* [String s] is a document that consists of just the string [s]. We - assume, but do not check, that this string does not contain a newline - character. [String] is a special case of [FancyString], which takes up - less space in memory. *) - - | String of string - - (* [FancyString (s, ofs, len, apparent_length)] is a (portion of a) string - that may contain fancy characters: color escape characters, UTF-8 or - multi-byte characters, etc. Thus, the apparent length (which corresponds - to what will be visible on screen) differs from the length (which is a - number of bytes, and is reported by [String.length]). We assume, but do - not check, that fancystrings do not contain a newline character. *) - - | FancyString of string * int * int * int - - (* [Blank n] is a document that consists of [n] blank characters. *) - - | Blank of int - - (* When in flattening mode, [IfFlat (d1, d2)] turns into the document - [d1]. When not in flattening mode, it turns into the document [d2]. *) - - | IfFlat of document * document - - (* When in flattening mode, [HardLine] causes a failure, which requires - backtracking all the way until the stack is empty. When not in flattening - mode, it represents a newline character, followed with an appropriate - number of indentation. A common way of using [HardLine] is to only use it - directly within the right branch of an [IfFlat] construct. *) - - | HardLine - - (* The following constructors store their space requirement. This is the - document's apparent length, if printed in flattening mode. This - information is computed in a bottom-up manner when the document is - constructed. *) - - (* In other words, the space requirement is the number of columns that the - document needs in order to fit on a single line. We express this value in - the set of `integers extended with infinity', and use the value - [infinity] to indicate that the document cannot be printed on a single - line. *) - - (* Storing this information at [Group] nodes is crucial, as it allows us to - avoid backtracking and buffering. *) - - (* Storing this information at other nodes allows the function [requirement] - to operate in constant time. This means that the bottom-up computation of - requirements takes linear time. *) - - (* [Cat (req, doc1, doc2)] is the concatenation of the documents [doc1] and - [doc2]. The space requirement [req] is the sum of the requirements of - [doc1] and [doc2]. *) - - | Cat of requirement * document * document - - (* [Nest (req, j, doc)] is the document [doc], in which the indentation - level has been increased by [j], that is, in which [j] blanks have been - inserted after every newline character. The space requirement [req] is - the same as the requirement of [doc]. *) - - | Nest of requirement * int * document - - (* [Group (req, doc)] represents an alternative: it is either a flattened - form of [doc], in which occurrences of [Group] disappear and occurrences - of [IfFlat] resolve to their left branch, or [doc] itself. The space - requirement [req] is the same as the requirement of [doc]. *) - - | Group of requirement * document - - (* [Align (req, doc)] increases the indentation level to reach the current - column. Thus, the document [doc] is rendered within a box whose upper - left corner is the current position. The space requirement [req] is the - same as the requirement of [doc]. *) - - | Align of requirement * document - - (* [Range (req, hook, doc)] is printed like [doc]. After it is printed, the - function [hook] is applied to the range that is occupied by [doc] in the - output. *) - - | Range of requirement * (range -> unit) * document - - (* [Custom (req, f)] is a document whose appearance is user-defined. *) - - | Custom of custom - -(* ------------------------------------------------------------------------- *) - -(* Retrieving or computing the space requirement of a document. *) - -let rec requirement = function - | Empty -> - 0 - | Char _ -> - 1 - | String s -> - String.length s - | FancyString (_, _, _, len) - | Blank len -> - len - | IfFlat (doc1, _) -> - (* In flattening mode, the requirement of [ifflat x y] is just the - requirement of its flat version, [x]. *) - (* The smart constructor [ifflat] ensures that [IfFlat] is never nested - in the left-hand side of [IfFlat], so this recursive call is not a - problem; the function [requirement] has constant time complexity. *) - requirement doc1 - | HardLine -> - (* A hard line cannot be printed in flattening mode. *) - infinity - | Cat (req, _, _) - | Nest (req, _, _) - | Group (req, _) - | Align (req, _) - | Range (req, _, _) -> - (* These nodes store their requirement -- which is computed when the - node is constructed -- so as to allow us to answer in constant time - here. *) - req - | Custom c -> - c#requirement - -(* ------------------------------------------------------------------------- *) - -(* The above algebraic data type is not exposed to the user. Instead, we - expose the following functions. These functions construct a raw document - and compute its requirement, so as to obtain a document. *) - -let empty = - Empty - -let char c = - assert (c <> '\n'); - Char c - -let space = - Blank 1 - -let string s = - String s - -let fancysubstring s ofs len apparent_length = - if len = 0 then - empty - else - FancyString (s, ofs, len, apparent_length) - -let substring s ofs len = - fancysubstring s ofs len len - -let fancystring s apparent_length = - fancysubstring s 0 (String.length s) apparent_length - -(* The following function was stolen from [Batteries]. *) -let utf8_length s = - let rec length_aux s c i = - if i >= String.length s then c else - let n = Char.code (String.unsafe_get s i) in - let k = - if n < 0x80 then 1 else - if n < 0xe0 then 2 else - if n < 0xf0 then 3 else 4 - in - length_aux s (c + 1) (i + k) - in - length_aux s 0 0 - -let utf8string s = - fancystring s (utf8_length s) - -let utf8format f = - Printf.ksprintf utf8string f - -let hardline = - HardLine - -let blank n = - match n with - | 0 -> - empty - | _ -> - Blank n - -let ifflat doc1 doc2 = - (* Avoid nesting [IfFlat] in the left-hand side of [IfFlat], as this - is redundant. *) - match doc1 with - | IfFlat (doc1, _) - | doc1 -> - IfFlat (doc1, doc2) - -let internal_break i = - ifflat (blank i) hardline - -let break0 = - internal_break 0 - -let break1 = - internal_break 1 - -let break i = - match i with - | 0 -> - break0 - | 1 -> - break1 - | _ -> - internal_break i - -let (^^) x y = - match x, y with - | Empty, _ -> - y - | _, Empty -> - x - | _, _ -> - Cat (requirement x ++ requirement y, x, y) - -let nest i x = - assert (i >= 0); - Nest (requirement x, i, x) - -let group x = - let req = requirement x in - (* Minor optimisation: an infinite requirement dissolves a group. *) - if req = infinity then - x - else - Group (req, x) - -let align x = - Align (requirement x, x) - -let range hook x = - Range (requirement x, hook, x) - -let custom c = - (* Sanity check. *) - assert (c#requirement >= 0); - Custom c - -(* ------------------------------------------------------------------------- *) - -(* This function expresses the following invariant: if we are in flattening - mode, then we must be within bounds, i.e. the width and ribbon width - constraints must be respected. *) - -let ok state flatten : bool = - not flatten || - state.column <= state.width && state.column <= state.last_indent + state.ribbon - -(* ------------------------------------------------------------------------- *) - -(* The pretty rendering engine. *) - -(* The renderer is supposed to behave exactly like Daan Leijen's, although its - implementation is quite radically different, and simpler. Our documents are - constructed eagerly, as opposed to lazily. This means that we pay a large - space overhead, but in return, we get the ability of computing information - bottom-up, as described above, which allows to render documents without - backtracking or buffering. *) - -(* The [state] record is never copied; it is just threaded through. In - addition to it, the parameters [indent] and [flatten] influence the - manner in which the document is rendered. *) - -(* The code is written in tail-recursive style, so as to avoid running out of - stack space if the document is very deep. Each [KCons] cell in a - continuation represents a pending call to [pretty]. Each [KRange] cell - represents a pending call to a user-provided range hook. *) - -type cont = - | KNil - | KCons of int * bool * document * cont - | KRange of (range -> unit) * point * cont - -let rec pretty - (output : output) - (state : state) - (indent : int) - (flatten : bool) - (doc : document) - (cont : cont) -: unit = - match doc with - - | Empty -> - continue output state cont - - | Char c -> - output#char c; - state.column <- state.column + 1; - (* assert (ok state flatten); *) - continue output state cont - - | String s -> - let len = String.length s in - output#substring s 0 len; - state.column <- state.column + len; - (* assert (ok state flatten); *) - continue output state cont - - | FancyString (s, ofs, len, apparent_length) -> - output#substring s ofs len; - state.column <- state.column + apparent_length; - (* assert (ok state flatten); *) - continue output state cont - - | Blank n -> - blanks output n; - state.column <- state.column + n; - (* assert (ok state flatten); *) - continue output state cont - - | HardLine -> - (* We cannot be in flattening mode, because a hard line has an [infinity] - requirement, and we attempt to render a group in flattening mode only - if this group's requirement is met. *) - assert (not flatten); - (* Emit a hardline. *) - output#char '\n'; - blanks output indent; - state.line <- state.line + 1; - state.column <- indent; - state.last_indent <- indent; - (* Continue. *) - continue output state cont - - | IfFlat (doc1, doc2) -> - (* Pick an appropriate sub-document, based on the current flattening - mode. *) - pretty output state indent flatten (if flatten then doc1 else doc2) cont - - | Cat (_, doc1, doc2) -> - (* Push the second document onto the continuation. *) - pretty output state indent flatten doc1 (KCons (indent, flatten, doc2, cont)) - - | Nest (_, j, doc) -> - pretty output state (indent + j) flatten doc cont - - | Group (req, doc) -> - (* If we already are in flattening mode, stay in flattening mode; we - are committed to it. If we are not already in flattening mode, we - have a choice of entering flattening mode. We enter this mode only - if we know that this group fits on this line without violating the - width or ribbon width constraints. Thus, we never backtrack. *) - let flatten = - flatten || - let column = state.column ++ req in - column <== state.width && column <== state.last_indent + state.ribbon - in - pretty output state indent flatten doc cont - - | Align (_, doc) -> - (* The effect of this combinator is to set [indent] to [state.column]. - Usually [indent] is equal to [state.last_indent], hence setting it - to [state.column] increases it. However, if [nest] has been used - since the current line began, then this could cause [indent] to - decrease. *) - (* assert (state.column > state.last_indent); *) - pretty output state state.column flatten doc cont - - | Range (_, hook, doc) -> - let start : point = (state.line, state.column) in - pretty output state indent flatten doc (KRange (hook, start, cont)) - - | Custom c -> - (* Invoke the document's custom rendering function. *) - c#pretty output state indent flatten; - (* Sanity check. *) - assert (ok state flatten); - (* Continue. *) - continue output state cont - -and continue output state = function - | KNil -> - () - | KCons (indent, flatten, doc, cont) -> - pretty output state indent flatten doc cont - | KRange (hook, start, cont) -> - let finish : point = (state.line, state.column) in - hook (start, finish); - continue output state cont - -(* Publish a version of [pretty] that does not take an explicit continuation. - This function may be used by authors of custom documents. We do not expose - the internal [pretty] -- the one that takes a continuation -- because we - wish to simplify the user's life. The price to pay is that calls that go - through a custom document cannot be tail calls. *) - -let pretty output state indent flatten doc = - pretty output state indent flatten doc KNil - -(* ------------------------------------------------------------------------- *) - -(* The compact rendering algorithm. *) - -let rec compact output doc cont = - match doc with - | Empty -> - continue output cont - | Char c -> - output#char c; - continue output cont - | String s -> - let len = String.length s in - output#substring s 0 len; - continue output cont - | FancyString (s, ofs, len, _apparent_length) -> - output#substring s ofs len; - continue output cont - | Blank n -> - blanks output n; - continue output cont - | HardLine -> - output#char '\n'; - continue output cont - | Cat (_, doc1, doc2) -> - compact output doc1 (doc2 :: cont) - | IfFlat (doc, _) - | Nest (_, _, doc) - | Group (_, doc) - | Align (_, doc) - | Range (_, _, doc) -> - compact output doc cont - | Custom c -> - (* Invoke the document's custom rendering function. *) - c#compact output; - continue output cont - -and continue output cont = - match cont with - | [] -> - () - | doc :: cont -> - compact output doc cont - -let compact output doc = - compact output doc [] - -(* ------------------------------------------------------------------------- *) - -(* We now instantiate the renderers for the three kinds of output channels. *) - -(* This is just boilerplate. *) - -module type RENDERER = sig - type channel - type document - val pretty: float -> int -> channel -> document -> unit - val compact: channel -> document -> unit -end - -module MakeRenderer (X : sig - type channel - val output: channel -> output -end) -: RENDERER with type channel = X.channel and type document = document -= struct - type channel = X.channel - type nonrec document = document - let pretty rfrac width channel doc = pretty (X.output channel) (initial rfrac width) 0 false doc - let compact channel doc = compact (X.output channel) doc -end - -module ToChannel = - MakeRenderer(struct - type channel = out_channel - let output channel = new buffering (new channel_output channel) - end) - -module ToBuffer = - MakeRenderer(struct - type channel = Buffer.t - let output buffer = new buffering (new buffer_output buffer) - end) - -module ToFormatter = - MakeRenderer(struct - type channel = Format.formatter - let output fmt = new buffering (new formatter_output fmt) - end) diff --git a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.mli b/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.mli deleted file mode 100644 index 4d03b12..0000000 --- a/tests/menhir_tests/test10menhir_with_aux_rules/PPrintEngine.mli +++ /dev/null @@ -1,307 +0,0 @@ -(******************************************************************************) -(* *) -(* PPrint *) -(* *) -(* François Pottier, Inria Paris *) -(* Nicolas Pouillard *) -(* *) -(* Copyright 2007-2022 Inria. All rights reserved. This file is *) -(* distributed under the terms of the GNU Library General Public *) -(* License, with an exception, as described in the file LICENSE. *) -(* *) -(******************************************************************************) - -(**[PPrint] is an OCaml library for {b pretty-printing textual documents}. - It takes care of {b indentation and line breaks}, and is typically used - to {b pretty-print code}. *) - -(** {1:building Building Documents} *) - -(**The abstract type of documents. *) -type document - -(** {2 Atomic Documents} *) - -(**[empty] is the empty document. *) -val empty: document - -(**[char c] is an atomic document that consists of the single character [c]. - This character must not be a newline character. *) -val char: char -> document - -(**[string s] is an atomic document that consists of the string [s]. This - string must not contain a newline. The printing engine assumes that the - ideal width of this string is [String.length s]. This assumption is safe - if this is an ASCII string. Otherwise, {!fancystring} or {!utf8string} - should be preferred. *) -val string: string -> document - -(**[substring s ofs len] is an atomic document that consists of the portion - of the string [s] delimited by the offset [ofs] and the length [len]. - This portion must not contain a newline. [substring s ofs len] is - equivalent to [string (String.sub s ofs len)], but is expected to be more - efficient, as the substring is not actually extracted. *) -val substring: string -> int -> int -> document - -(**[fancystring s alen] is an atomic document that consists of the string - [s]. This string must not contain a newline. The string may contain fancy - characters: color escape characters, UTF-8 characters, etc. Thus, its - apparent length (which measures how many columns the text will take up on - screen) differs from its length in bytes. The printing engine assumes - that its apparent length is [alen]. *) -val fancystring: string -> int -> document - -(**[fancysubstring s ofs len alen] is equivalent to [fancystring (String.sub - s ofs len) alen]. *) -val fancysubstring : string -> int -> int -> int -> document - -(**[utf8string s] is an atomic document that consists of the UTF-8-encoded - string [s]. This string must not contain a newline. [utf8string s] is - equivalent to [fancystring s (utf8_length s)], where [utf8_length s] is - the apparent length of the UTF-8-encoded string [s]. *) -val utf8string: string -> document - -(** [utf8format format ...] is equivalent to - [utf8string (Printf.sprintf format ...)]. *) -val utf8format: ('a, unit, string, document) format4 -> 'a - -(** {2 Blanks and Newlines} *) - -(**The atomic document [hardline] represents a forced newline. This document - has infinite ideal width: thus, if there is a choice between printing it - in flat mode and printing it in normal mode, normal mode is preferred. In - other words, when [hardline] is placed directly inside a group, this - group is dissolved: [group hardline] is equivalent to [hardline]. This - combinator should be seldom used; consider using {!break} instead. *) -val hardline: document - -(**The atomic document [blank n] consists of [n] blank characters. A blank - character is like an ordinary ASCII space character [char ' '], except - that blank characters that appear at the end of a line are automatically - suppressed. *) -val blank: int -> document - -(**[space] is a synonym for [blank 1]. It consists of one blank character. - It is therefore not equivalent to [char ' ']. *) -val space: document - -(**The document [break n] is a breakable blank of width [n]. It produces [n] - blank characters if the printing engine is in flat mode, and a single - newline character if the printing engine is in normal mode. [break 1] is - equivalent to [ifflat (blank 1) hardline]. *) -val break: int -> document - -(** {2 Composite Documents} *) - -(**[doc1 ^^ doc2] is the concatenation of the documents [doc1] and [doc2]. *) -val (^^): document -> document -> document - -(**[group doc] encodes a choice. If the document [doc] fits on the current - line, then it is rendered on a single line, in flat mode. (All [group] - combinators inside it are then ignored.) Otherwise, this group is - dissolved, and [doc] is rendered in normal mode. There might be more - groups within [doc], whose presence leads to further choices being - explored. *) -val group: document -> document - -(**[ifflat doc1 doc2] is rendered as [doc1] if the printing engine is in - flat mode, that is, if the printing engine has determined that some - enclosing group fits on the current line. Otherwise, it is rendered as - [doc2]. Use this combinator with caution! Because the printing engine is - free to choose between [doc1] and [doc2], these documents must be - semantically equivalent. It is up to the user to enforce this property. *) -val ifflat: document -> document -> document - -(**To render the document [nest j doc], the printing engine temporarily - increases the current indentation level by [j], then renders [doc]. The - effect of the current indentation level is as follows: every time a - newline character is emitted, it is immediately followed by [n] blank - characters, where [n] is the current indentation level. Thus, one may - think of [nest j doc] roughly as the document [doc] in which [j] blank - characters have been inserted after every newline character. *) -val nest: int -> document -> document - -(**To render [align doc], the printing engine sets the current indentation - level to the current column, then renders [doc]. In other words, the - document [doc] is rendered within a box whose upper left corner is the - current position of the printing engine. *) -val align: document -> document - -(**A point is a pair of a line number and a column number. *) -type point = - int * int - -(**A range is a pair of points. *) -type range = - point * point - -(**The document [range hook doc] is printed like the document [doc], but - allows the caller to register a hook that is applied, when the document - is printed, to the range occupied by this document in the output text. - This offers a way of mapping positions in the output text back to - (sub)documents. *) -val range: (range -> unit) -> document -> document - -(** {1:rendering Rendering Documents} *) - -(**Three renderers are available. They offer the same API, described - by the signature {!RENDERER}, and differ only in the nature of the - output channel that they use. *) - -(**This signature describes the document renderers in a manner that - is independent of the type of the output channel. *) -module type RENDERER = sig - - (**The type of the output channel. *) - type channel - - (**The type of documents. *) - type document - - (** [pretty rfrac width channel document] pretty-prints the document - [document] into the output channel [channel]. The parameter [width] is - the maximum number of characters per line. The parameter [rfrac] is the - ribbon width, a fraction relative to [width]. The ribbon width is the - maximum number of non-indentation characters per line. *) - val pretty: float -> int -> channel -> document -> unit - - (** [compact channel document] prints the document [document] to the output - channel [channel]. No indentation is used. All newline instructions are - respected, that is, no groups are flattened. *) - val compact: channel -> document -> unit - -end - -(**This renderer sends its output into an output channel. *) -module ToChannel : RENDERER - with type channel = out_channel - and type document = document - -(**This renderer sends its output into a memory buffer. *) -module ToBuffer : RENDERER - with type channel = Buffer.t - and type document = document - -(**This renderer sends its output into a formatter channel. *) -module ToFormatter : RENDERER - with type channel = Format.formatter - and type document = document - -(** {1:defining Defining Custom Documents} *) - -(**It is possible to define custom document constructors, provided they meet - the expectations of the printing engine. In short, the custom document - combinator {!val-custom} expects an object of class {!class-type-custom}. - This object must provide three methods. The method [requirement] must - compute the ideal width of the custom document. The methods [pretty] and - [compact] must render the custom document. For this purpose, they have - access to the {{!output}output channel} and to the {{!state}state} of the - printing engine. *) - -(** A width requirement is expressed as an integer. The value [max_int] - is reserved and represents infinity. *) -type requirement = int - -(**[infinity] represents an infinite width requirement. *) -val infinity : requirement - -(**An output channel is abstractly represented as an object equipped with - methods for displaying one character and for displaying a substring. *) -class type output = object - - (**[char c] sends the character [c] to the output channel. *) - method char: char -> unit - - (**[substring s ofs len] sends the substring of [s] delimited by the - offset [ofs] and the length [len] to the output channel. *) - method substring: string -> int (* offset *) -> int (* length *) -> unit - -end - -(**The internal state of the rendering engine is exposed to the user who - wishes to define custom documents. However, its structure is subject to - change in future versions of the library. *) -type state = { - - width: int; - (** The line width. This parameter is fixed throughout the execution of - the renderer. *) - - ribbon: int; - (** The ribbon width. This parameter is fixed throughout the execution of - the renderer. *) - - mutable last_indent: int; - (** The number of blanks that were printed at the beginning of the current - line. This field is updated (only) when a hardline is emitted. It is - used (only) to determine whether the ribbon width constraint is - respected. *) - - mutable line: int; - (** The current line. This field is updated (only) when a hardline is - emitted. It is not used by the pretty-printing engine itself. *) - - mutable column: int; - (** The current column. This field must be updated whenever something is - sent to the output channel. It is used (only) to determine whether the - width constraint is respected. *) - - } - -(**A custom document is defined by implementing an object of class - {!class-type-custom}. *) -class type custom = object - - (**A custom document must publish the width (i.e., the number of columns) - that it would like to occupy if printed on a single line (in flat - mode). The special value [infinity] means that this document cannot be - printed on a single line; this value causes any groups that contain - this document to be dissolved. This method should in principle work in - constant time. *) - method requirement: requirement - - (**The method [pretty] is used by the main rendering algorithm. It has - access to the output channel and to the printing engine's internal - state. In addition, it receives the current indentation level and a - Boolean flag that tells whether the engine is currently in flat mode. - If the engine is in flat mode, then the document must be printed on a - single line, in a manner that is consistent with the width requirement - that was published ahead of time. If the engine is in normal mode, then - there is no such obligation. The state must be updated in a manner that - is consistent with what is sent to the output channel. *) - method pretty: output -> state -> int -> bool -> unit - - (**The method [compact] is used by the compact rendering algorithm. It - has access to the output channel only. *) - method compact: output -> unit - -end - -(**[custom] constructs a custom document out an object of type - {!class-type-custom}. *) -val custom: custom -> document - -(**Some of the key functions of the library are exposed, in the hope that - they may be useful to authors of custom (leaf and composite) documents. - In the case of a leaf document, they can help perform certain basic - functions; for instance, applying the function {!pretty} to the document - {!hardline} is a simple way of printing a hardline, while respecting the - indentation parameters and updating the state in a correct manner. - Similarly, applying {!pretty} to the document [blank n] is a simple way - of printing [n] blank characters. In the case of a composite document - (one that contains subdocuments), these functions are essential: they - allow computing the width requirement of a subdocument and displaying a - subdocument. *) - -(**[requirement doc] computes the width requirement of the document [doc]. - It runs in constant time. *) -val requirement: document -> requirement - -(**[pretty output state indent flatten doc] prints the document [doc]. See - the documentation of the method [pretty] in the class - {!class-type-custom}. *) -val pretty: output -> state -> int -> bool -> document -> unit - -(**[compact output doc] prints the document [doc]. See the documentation of - the method [compact] in the class {!class-type-custom}. *) -val compact: output -> document -> unit diff --git a/tests/menhir_tests/test10menhir_with_aux_rules/pprint-master.zip b/tests/menhir_tests/test10menhir_with_aux_rules/pprint-master.zip deleted file mode 100644 index a6e0ac3e6613c585f739adaac12f53ab36d8f9a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51643 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¡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%0AGH%=XRPiP`|<5CdFw>T5oVrv300B6ksdO3A6xq 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 From b050336d9f5d4791e6f1db5be9a2073f4114e6cf Mon Sep 17 00:00:00 2001 From: pes20 Date: Fri, 4 Mar 2022 09:34:52 +0000 Subject: [PATCH 19/46] make README slightly more informative --- regression/README | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/regression/README b/regression/README index 29720af..a05a9fd 100644 --- a/regression/README +++ b/regression/README @@ -1,3 +1,24 @@ +This is infrastructure for regression testing the Ott implementation (by FZN, but this README is much later by PS; it might be wrong). + +`./regression`, from `regression.ml`, runs Ott on a set of tests and runs Coq, Isabelle, HOL4, OCaml, and Latex on the generated code. + + + +This is infrastructure for regression testing the Ott implementation (by FZN, but this README is much later by PS). + +`./regression`, from `regression.ml`, runs Ott on a set of tests and runs Coq, Isabelle, HOL4, OCaml, and Latex on the generated code. For some tests, not all of those are intended to work, so this is set up to either compute and save a baseline or to compare against such a baseline. Baselines are in some internal `.bl` format, but can be pretty-printed with the `-dump_baseline` option. Here in each backend column the first `+`/`-` records whether Ott successfully generated that output and the second `+`/`-` whether the relevant tool successfully built it. + +``` +Coq CoqNL Isa HOL OCaml LaTeX +[...] + + + + - + - + + + + + + ../tests/test10.ott + + + + - + - + + - + + ../tests/test10st.ott +[...] +``` + + +`./regression -help` shows the command-line options. + $ ./regression -baseline -todo_list From 88e93b3529a71b0eb126f3199464e1d226c3bc29 Mon Sep 17 00:00:00 2001 From: Kazuhiko Sakaguchi Date: Wed, 27 Oct 2021 14:39:58 +0900 Subject: [PATCH 20/46] coq-ott compatible with Coq 8.14 --- coq/.gitignore | 1 + coq/_CoqProject | 2 -- coq/ott_list_base.v | 4 ++-- coq/ott_list_distinct.v | 10 +++++----- coq/ott_list_nth.v | 19 +++++++++---------- coq/ott_list_repeat.v | 6 +++--- coq/ott_list_support.v | 4 ++-- coq/ott_list_takedrop.v | 20 ++++++++++---------- 8 files changed, 32 insertions(+), 34 deletions(-) diff --git a/coq/.gitignore b/coq/.gitignore index c8f46e0..0c761dd 100644 --- a/coq/.gitignore +++ b/coq/.gitignore @@ -5,6 +5,7 @@ *.glob *.v.d *.aux +.lia.cache Makefile.coq Makefile.coq.conf .Makefile.coq.d diff --git a/coq/_CoqProject b/coq/_CoqProject index 0f057ca..6f23a74 100644 --- a/coq/_CoqProject +++ b/coq/_CoqProject @@ -12,5 +12,3 @@ ott_list_nth.v ott_list_core.v ott_list_distinct.v ott_list_flat_map.v - --arg -w -arg -omega-is-deprecated diff --git a/coq/ott_list_base.v b/coq/ott_list_base.v index 677df1d..73cdce9 100644 --- a/coq/ott_list_base.v +++ b/coq/ott_list_base.v @@ -2,7 +2,7 @@ Require Import Arith. Require Import List. -Require Import Omega. +Require Import Lia. Require Import Wf_nat. Require Import Ott.ott_list_support. @@ -113,7 +113,7 @@ Lemma app_inj_suffix_length_prefix : Proof. intros. eapply app_inj_prefix_length_prefix. 2: eexact H0. assert (Eq := f_equal (@length A) H0). - repeat rewrite length_app in Eq. omega. + repeat rewrite length_app in Eq. lia. Qed. Lemma app_inj_suffix_length_suffix : forall l0 l1 l0' l1', diff --git a/coq/ott_list_distinct.v b/coq/ott_list_distinct.v index 3ee0a84..70349df 100644 --- a/coq/ott_list_distinct.v +++ b/coq/ott_list_distinct.v @@ -8,7 +8,7 @@ Require Import Ott.ott_list_core. Require Import Ott.ott_list_base. Require Import Ott.ott_list_nth. Require Import Ott.ott_list_mem. -Require Import Omega. +Require Import Lia. @@ -164,15 +164,15 @@ Proof. generalize (j-i); clear Ineq j; intros k Distinct Nths Bound. destruct k. solve [auto with arith]. elimtype False. generalize dependent xs; induction i; intros; destruct xs; simpl in * . - omega. + lia. destruct (andb_prop2 _ _ Distinct) as [Notin _]; clear Distinct Bound. generalize dependent k; induction xs; intros; simpl in * . destruct k; discriminate. destruct (eq_dec a0 a). exact Notin. destruct k; simpl in *; [injection Nths | idtac]; solve [eauto]. - omega. + lia. eapply IHi; eauto. destruct_andb; assumption. - omega. + lia. Qed. Lemma all_distinct_indices : forall xs i j, @@ -183,7 +183,7 @@ Lemma all_distinct_indices : Proof. intros. destruct (le_ge_dec i j). eapply all_distinct_indices_aux; eauto. - symmetry. eapply all_distinct_indices_aux; eauto. omega. + symmetry. eapply all_distinct_indices_aux; eauto. lia. Qed. End All_distinct. diff --git a/coq/ott_list_nth.v b/coq/ott_list_nth.v index 46c1d17..12c14bf 100644 --- a/coq/ott_list_nth.v +++ b/coq/ott_list_nth.v @@ -1,5 +1,5 @@ Require Import Arith. -Require Import Omega. +Require Import Lia. Require Import List. Require Import Ott.ott_list_support. Require Import Ott.ott_list_base. @@ -92,8 +92,8 @@ Lemma nth_error_out : forall l n, length l <= n -> nth_error l n = error. Proof. induction l; intros n H. solve [apply nth_error_nil]. - simpl in H. destruct n. assert False; [omega | intuition]. - simpl. apply IHl. omega. + simpl in H. destruct n. assert False; [lia | intuition]. + simpl. apply IHl. lia. Qed. Lemma nth_error_app_prefix : @@ -128,7 +128,7 @@ Proof. destruct (le_lt_dec (length l) n); destruct (le_lt_dec (S (length l)) (S n)); reflexivity || - (assert False; [omega | intuition]) || + (assert False; [lia | intuition]) || simpl. match match goal with |- ?g => g end with value ?lhs = value ?rhs => assert (Eq : lhs=rhs) @@ -162,11 +162,10 @@ Lemma nth_eq_nth_safe : end. Proof. induction l; destruct n; reflexivity || intros. simpl nth; simpl length. - destruct (le_lt_dec (S (length l)) (S n)); - case_eq (le_lt_dec (length l) n); intros; - [idtac | elimtype False; omega | elimtype False; omega | idtac]; - pose (H' := IHl n default); rewrite H in H'; rewrite H'. - reflexivity. simpl; apply nth_safe_proof_irrelevance. + rewrite (IHl n default). + simpl; destruct (le_lt_dec (length l) n); simpl. + reflexivity. + apply nth_safe_proof_irrelevance. Qed. Lemma nth_eq_nth_error : @@ -189,6 +188,6 @@ Arguments nth_safe_app [A] _ _ _ _. Hint Rewrite nth_map nth_ok_map nth_error_map : lists. Hint Rewrite nth_error_nil : lists. -Hint Rewrite nth_error_in nth_error_out using omega : list_nth_error. +Hint Rewrite nth_error_in nth_error_out using lia : list_nth_error. Hint Rewrite nth_error_dec : list_nth_dec. Hint Resolve nth_error_value nth_error_error : datatypes. diff --git a/coq/ott_list_repeat.v b/coq/ott_list_repeat.v index d024a46..dc6b86d 100644 --- a/coq/ott_list_repeat.v +++ b/coq/ott_list_repeat.v @@ -2,7 +2,7 @@ Require Import Arith. Require Import List. -Require Import Omega. +Require Import Lia. Require Import Ott.ott_list_support. Require Import Ott.ott_list_base. Require Import Ott.ott_list_nth. @@ -43,7 +43,7 @@ Qed. Lemma repeat_S : forall n x, repeat (S n) x = repeat n x ++ x::nil. Proof. - intros. replace (S n) with (n+1). 2: omega. + intros. replace (S n) with (n+1). 2: lia. rewrite repeat_app. reflexivity. Qed. @@ -71,7 +71,7 @@ Proof. intros. assert (value (nth_safe (repeat n x) m H) = value x). rewrite nth_safe_eq_nth_error. rewrite nth_error_repeat. rewrite repeat_length in H. - destruct (le_lt_dec n m). elimtype False; omega. reflexivity. + destruct (le_lt_dec n m). lia. reflexivity. injection H0. tauto. Qed. diff --git a/coq/ott_list_support.v b/coq/ott_list_support.v index dabae59..e3185e2 100644 --- a/coq/ott_list_support.v +++ b/coq/ott_list_support.v @@ -1,7 +1,7 @@ (* Additional definitions and lemmas on lists *) Require Import Arith. -Require Import Omega. +Require Import Lia. @@ -15,7 +15,7 @@ Lemma le_lt_dec_S : (if le_lt_dec n m then x else y). Proof. intros. destruct (le_lt_dec n m); destruct (le_lt_dec (S n) (S m)). - reflexivity. elimtype False; omega. elimtype False; omega. reflexivity. + reflexivity. lia. lia. reflexivity. Qed. End List_lib_Arith. diff --git a/coq/ott_list_takedrop.v b/coq/ott_list_takedrop.v index ff21685..4656165 100644 --- a/coq/ott_list_takedrop.v +++ b/coq/ott_list_takedrop.v @@ -4,7 +4,7 @@ Require Import Arith. Require Import Max. Require Import Min. Require Import List. -Require Import Omega. +Require Import Lia. Require Import Ott.ott_list_support. Require Import Ott.ott_list_base. Require Import Ott.ott_list_nth. @@ -47,7 +47,7 @@ Lemma take_all : Proof. induction l; destruct n; intros; try reflexivity. solve [inversion H]. - simpl in * . apply (f_equal2 (@cons A)). reflexivity. apply IHl. omega. + simpl in * . apply (f_equal2 (@cons A)). reflexivity. apply IHl. lia. Qed. Lemma take_length : @@ -101,7 +101,7 @@ Lemma drop_all : Proof. induction l; destruct n; intros; try reflexivity. solve [inversion H]. - simpl in * . apply IHl. omega. + simpl in * . apply IHl. lia. Qed. Lemma drop_length : forall l n, length (drop n l) = length l - n. @@ -120,7 +120,7 @@ Proof. generalize (conj (refl_equal (length (drop n l))) (refl_equal (drop n l))). pattern (drop n l) at 1 3. case (drop n l); intros; rewrite drop_length in H; destruct H; simpl in * . - elimtype False; omega. + lia. rewrite <- H0. assumption. Qed. @@ -193,7 +193,7 @@ Proof. intros. induction l; simpl; congruence. Qed. Lemma take_from_app : forall l l', take (length l) (l ++ l') = l. Proof. - intros. replace (length l) with (length l + 0). 2: omega. + intros. replace (length l) with (length l + 0). 2: lia. rewrite take_app_short. rewrite take_0. symmetry. apply app_nil_end. Qed. @@ -201,7 +201,7 @@ Qed. Lemma drop_from_app : forall l l', drop (length l) (l ++ l') = l'. Proof. - intros. replace (length l) with (length l + 0). 2: omega. + intros. replace (length l) with (length l + 0). 2: lia. rewrite drop_app_short. apply drop_0. Qed. @@ -267,7 +267,7 @@ Ltac cut_list original cut_point prefix suffix := autorewrite with lists take_drop; intros; rename p into prefix; rename s into suffix | (**length original < n**) - try (equate_list_lengths; elimtype False; omega) ] + try (equate_list_lengths; lia) ] ). (* Look for equations between lists that can be simplified. @@ -280,14 +280,14 @@ Ltac parallel_split := | H : app ?p ?s = app ?p' ?s' |- _ => ( assert (tmp : length p = length p'); - [equate_list_lengths; omega | idtac]; + [equate_list_lengths; lia | idtac]; assert (EqPrefix := app_inj_prefix_length_prefix _ _ _ _ tmp H); rewrite <- EqPrefix in H; assert (EqSuffix := app_inj_prefix _ _ _ H); clear tmp H ) || ( assert (tmp : length s = length s'); - [equate_list_lengths; omega | idtac]; + [equate_list_lengths; lia | idtac]; assert (EqSuffix := app_inj_prefix_length_suffix _ _ _ _ tmp H); rewrite <- EqSuffix in H; assert (EqPrefix := app_inj_suffix _ _ _ H); @@ -303,7 +303,7 @@ Ltac parallel_split_maps := repeat match goal with | H : map ?f ?p ++ map ?f ?s = ?p' ++ ?s' |- _ => assert (Eqlen' : length (map f p) = length p'); - [equate_list_lengths; omega | idtac]; + [equate_list_lengths; lia | idtac]; assert (EqPrefix := app_inj_prefix_length_prefix _ _ _ _ Eqlen' H); rewrite <- EqPrefix in H; assert (EqSuffix := app_inj_prefix _ _ _ H); From 5b5f033dce44a062082d4e18f1ccc9f684db1e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20P=C3=A9rami?= Date: Mon, 7 Mar 2022 13:42:30 +0000 Subject: [PATCH 21/46] Fix regression machinery for Coq --- regression/Makefile | 3 +++ regression/_ott_coqrc.v | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/regression/Makefile b/regression/Makefile index dc26d69..da75cef 100644 --- a/regression/Makefile +++ b/regression/Makefile @@ -66,6 +66,9 @@ clean: rm -rf *~ rm -rf regression rm -rf testRegr* + rm -rf .testRegr* + rm -rf ._ott_coqrc.aux + rm -rf _ott_coqrc.glob clean-regr: rm -rf testRegr* diff --git a/regression/_ott_coqrc.v b/regression/_ott_coqrc.v index 01f9979..c2714a0 100644 --- a/regression/_ott_coqrc.v +++ b/regression/_ott_coqrc.v @@ -1,6 +1,6 @@ (*This file is used by the regression tests.*) (* Add LoadPath "../coq/metatheory".*) -Add LoadPath "../coq". +Add LoadPath "../coq" as Ott. (* Add LoadPath "". *) (* Add LoadPath "../coq/ln_sets". *) (* Add LoadPath "../examples/tapl". *) From 1b613be4a3adc7eaa90a24c0cb794711acf458ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20P=C3=A9rami?= Date: Mon, 7 Mar 2022 13:42:54 +0000 Subject: [PATCH 22/46] Allow recent Coq version with coq-ott in opam --- coq-ott.opam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coq-ott.opam b/coq-ott.opam index bb011f2..56f3e18 100644 --- a/coq-ott.opam +++ b/coq-ott.opam @@ -17,7 +17,7 @@ this library. build: [make "-j%{jobs}%" "-C" "coq"] install: [make "-C" "coq" "install"] depends: [ - "coq" {(>= "8.5" & < "8.13~") | (= "dev")} + "coq" {>= "8.5"} ] tags: [ "category:Computer Science/Semantics and Compilation/Semantics" From 9adb532558440b5db09a55e977d0637dab8c3e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20P=C3=A9rami?= Date: Mon, 7 Mar 2022 16:40:27 +0000 Subject: [PATCH 23/46] Require minimum version of menhir --- ott.opam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ott.opam b/ott.opam index cb66f6e..0d95a93 100644 --- a/ott.opam +++ b/ott.opam @@ -8,7 +8,7 @@ bug-reports: "https://github.com/ott-lang/ott/issues" depends: [ "ocaml" {>= "4.02.0"} "pprint" {with-test} - "menhir" {with-test} + "menhir" {>= 20151112 && with-test} ] build: [ [make "world"] From ffc4521c5f9c351b05ae0eee40966d2f9f7f4dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20P=C3=A9rami?= Date: Mon, 7 Mar 2022 17:35:04 +0000 Subject: [PATCH 24/46] Add new regression baseline for March 2022 --- regression/baseline-2022-03.bl | Bin 0 -> 5196 bytes regression/baseline-2022-03.txt | 102 ++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 regression/baseline-2022-03.bl create mode 100644 regression/baseline-2022-03.txt diff --git a/regression/baseline-2022-03.bl b/regression/baseline-2022-03.bl new file mode 100644 index 0000000000000000000000000000000000000000..cb63249450702f3bdcd727216c61d5bca16dbaa5 GIT binary patch literal 5196 zcmb7|eUwyX9mjX>{Y^niOJD}5=MZbMZac&5vg`{;Ev6?AoHC0_v6wTvdzYPUc4n9t z-61GnkY0T0Atgymuf$rGNojfHq@+?>7N+HqO7lQudGz#VP?{!vo;&y6*_nIwEPs5s z7w-4>Jip)fd7j_B!{%+b?rCZ|p|`0?%%-Lpr#3YmcgVJT!o4 z#s<>s%IQ+Yok`CCa*}g#Zs*zdvijh$(R{2_NM(y&P(SK^9VD$v7V1#jUJ{6LvqLpc zNtcqjO6Dl(uvPel>$@?1( z>PWal#+0-xS?vt`b|vRcc3(T$kw}i@#@zCxM@hGmwMynI`Gk@S8xuyIE~bi=tUEgP zVa6IIZAylfSdCtEB#I?hy2?qn;MkE@k_#rn8Vy^ndQi!LlB|-Hk_}2KEbIJwBN#b3 zoG$R^NGdbzh9#ZkwsqLh3XPn2985O%sF7L}0m4OKgoTptiF zc7>aiY*ezd>VuM7Y`d#I;l%^4a+{Ltl-!}@T5M4AoxqS?u5hc9;XPFcl>9K@?vl8- z%r`nYK3J_<$&W*%Ug;A~mcLYT4RtH|d5F|2ewUNxlS;Nzxsu<6NWJoRDRh9dX-Xc!0`8(A2`_QQFX(Keky%Qfzy$7^L&RE?#M@VzR}2ON}j_A?pgxkZf}jFk?9DgK|;xkc)?vPMDDJ0G%_2(ELf`KRqWud zCq(Y9bu@Acf|KDacoRe+(A+w@Z}y#RsE z{}MP8R=}pfPV0$#4LBMZM4=1#BIMxn@VT%Ee~wS1*Z|i-1-=Yh!y^2(yBLKo;U>5o zz6LjjMfi(%DGFV~w_p^~FcB8vuilj?bPabx35u{Q#410jZ3uJ;_rhmj6YLHN@N>Eu zfv(^oa9rZLHzdGM={5wqfXBcwiR-?Q0DnW>g+S;3dvHABx<4eq-%$4<(E0xfZiNXr z7!u$f=xF3&1Umn}!S`Snybu!5TkQbR$m0lf{x5-U7rWs|NI;(#@Du{_Hw>=2W$cBc zfq>rH9)A`A`5VV`eH8X_KRqPC-+zBcME=H!Tz?Duxt|>p;IH({h{)fV&-IURko!{t z0eyZ-#@i6ggf?h~R#@2ht>X=E%-|`>8(j?jZ#cp|-%|BoM&2$m`g!_ZNWgM91C}+a zy)9(Clc%I@oC9xxb~w9H?d>09HBUbR^cdqj_z;}icxt+1C2QPWEn_W;PXbNGxDeLB z1&!l-+szo^={lgp7#0k{V58dGI>yI&`Z35uC(vJwvBs~7-n^H}Mjq2z44R`sTQSDr z;=qShax|4qb|we;J$&3raqJ|5Wby2D?DSSP0UE6kWeB-*!wp)vpVr(Fn%N*eEx}XiP1GHN<+-%#Y zYfY+k-hU{|p}HyHKl&xk4#7JY3FXYNLLa=Q_nF zfHq+K8u&VNYAKc7sej5p{sf)@j=u3UJmuEnBv~HN89o3yyXQcc`)7ETBdO;u|-eTXNVa^qFdC;h+ha&T1#(%GS8syv>w#`2|# zzbVXVDr&x&ke9edLGV6 zAwLP4xIfBuPB7iU-1?-ozMRVN3o$uf3xAD}{}J*ouC!Ziq`83bSpB?1)^Ijiu<}-_ zWDO;=xopWQjHR=wj6afBxVeZCPGBS)HroS*TVn$ZP|D?Nfx2iqrrAf(D}dojLRbCY{%u z1FH#dtGBBb?ik0;DwH!;t>mSIQLdSS7FYmlr#y`J<}lA9OjrLYh(SAyOnDgZm15Go z9Y5%2<{IdR0>iYvVNuSJt(9~>LE4qi!VvVs#wiZs7c@YS_T&pdCo@;V76xg5y-b|{ z`WP-}2TSQ(w$@wC41wB}YhfI=!ssy! Date: Mon, 7 Mar 2022 19:37:22 +0000 Subject: [PATCH 25/46] Fix tests/test1.ott for Isabelle --- tests/test1.ott | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test1.ott b/tests/test1.ott index 000009a..f6efcf5 100644 --- a/tests/test1.ott +++ b/tests/test1.ott @@ -33,7 +33,7 @@ grammar formula :: formula_ ::= | judgement :: :: judgement | not ( formula ) :: :: not - {{ isa not( [[formula]] ) }} + {{ isa ~( [[formula]] ) }} {{ coq not([[formula]]) }} {{ hol ~( [[formula]] ) }} {{ ocaml not([[formula]]) }} From 839c641fd0160123413a714ac1d283b4b4e8a6ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20P=C3=A9rami?= Date: Tue, 8 Mar 2022 11:12:49 +0000 Subject: [PATCH 26/46] Update baseline for test1 fix --- regression/baseline-2022-03.bl | Bin 5196 -> 5196 bytes regression/baseline-2022-03.txt | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/regression/baseline-2022-03.bl b/regression/baseline-2022-03.bl index cb63249450702f3bdcd727216c61d5bca16dbaa5..cee4b23399434448f6121865ec5ff8cd3804667b 100644 GIT binary patch delta 14 VcmX@3aYkc95+fte=48f~LI5fu1sebW delta 14 VcmX@3aYkc95+kGj=48f~LI5iB1wH@( diff --git a/regression/baseline-2022-03.txt b/regression/baseline-2022-03.txt index a4f4646..8dcf13a 100644 --- a/regression/baseline-2022-03.txt +++ b/regression/baseline-2022-03.txt @@ -5,7 +5,7 @@ + + + + - - - + - ../tests/test-mjp-trans.ott + - + - + - + - + - + + ../tests/test-mjp.ott + + + + + + + - + + + + ../tests/test-pottier1.ott - + + + + + - + - + - + + ../tests/test1.ott + + + + + + + + - + - + + ../tests/test1.ott + + + + + + + - + + + + ../tests/test10.ott + + + + + + + - - + + ../tests/test10_homs.ott + + + + + + - - + + ../tests/test10_isasyn.ott From d6e329f0fba0ee297c29332c95bea906acbd03dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20van=20Br=C3=BCgge?= Date: Tue, 7 Jul 2020 16:22:30 +0200 Subject: [PATCH 27/46] Fix addition of extra pipe characters with isasyn --- src/defns.ml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/defns.ml b/src/defns.ml index 37f99d1..501243c 100644 --- a/src/defns.ml +++ b/src/defns.ml @@ -481,9 +481,6 @@ let pp_defn fd (m:pp_mode) (xd:syntaxdefn) lookup (defnclass_wrapper:string) (un output_string fd "\n\n" | Isa io -> Printf.fprintf fd "(* defn %s *)\n\n" d.d_name; - ( match (Grammar_pp.isa_fancy_syntax_clause_for_defn m xd io "foo" defnclass_wrapper d) with - | None -> () - | Some _ -> output_string fd "| " ); iter_sep (pp_processed_semiraw_rule fd m xd) "\n| " d.d_rules | Hol _ -> Printf.fprintf fd "(* defn %s *)\n\n" d.d_name; @@ -595,7 +592,8 @@ let pp_defnclass fd (m:pp_mode) (xd:syntaxdefn) lookup (dc:defnclass) = output_string fd "\nwhere\n"; ( match fancy_syntax_clauses with | [] -> () - | fscs -> iter_asep fd "\n| " (*List.iter*) (fun x -> output_string fd (snd x)) fscs); + | fscs -> iter_asep fd "\n| " (*List.iter*) (fun x -> output_string fd (snd x)) fscs; + output_string fd "\n\n| "); iter_asep fd "\n| " (fun d -> pp_defn fd m xd lookup dc.dc_wrapper universe d) dc.dc_defns | Hol ho -> From 94cc0c63082a5ce622408ba485252e3c42ef754b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20P=C3=A9rami?= Date: Wed, 9 Mar 2022 15:07:59 +0000 Subject: [PATCH 28/46] Fix opam file --- ott.opam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ott.opam b/ott.opam index 0d95a93..9a9036e 100644 --- a/ott.opam +++ b/ott.opam @@ -8,7 +8,7 @@ bug-reports: "https://github.com/ott-lang/ott/issues" depends: [ "ocaml" {>= "4.02.0"} "pprint" {with-test} - "menhir" {>= 20151112 && with-test} + "menhir" {>= "20151112" & with-test} ] build: [ [make "world"] From 8c333f1f4149923f27672eb2343d7723c4872ac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20P=C3=A9rami?= Date: Wed, 9 Mar 2022 16:01:33 +0000 Subject: [PATCH 29/46] Bump to version 0.32 --- revision_history.txt | 8 ++++++++ src/Makefile | 2 +- src/tmp_date.txt | 2 +- src/version.ml | 4 ++-- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/revision_history.txt b/revision_history.txt index 6296965..ab10dd1 100644 --- a/revision_history.txt +++ b/revision_history.txt @@ -549,3 +549,11 @@ Peter Sewell + Thibaut Pérami: Add "menhir-start-type" hom to specify the top l of a menhir parser 2020-06-15 Version 0.31 + +2020-10 @mpwassell: Add experimental JSON pretty printing + +2022-03 @fpottier: Support most recent version of PPrint + +2022-03 @pi8027: Support recent Coq versions tested up to 8.15 + +2022-03-09 Version 0.32 diff --git a/src/Makefile b/src/Makefile index 7783c98..2364ba1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -33,7 +33,7 @@ topdir = .. -OTTVER=0.31 +OTTVER=0.32 # for us OCAMLC = ocamlc -g -dtypes # -w p disables partial match warnings diff --git a/src/tmp_date.txt b/src/tmp_date.txt index d00bf88..199b85d 100644 --- a/src/tmp_date.txt +++ b/src/tmp_date.txt @@ -1 +1 @@ -Mon 15 Jun 12:00:29 BST 2020 +Wed 09 Mar 2022 15:44:32 GMT diff --git a/src/version.ml b/src/version.ml index ae70274..d91f1bb 100644 --- a/src/version.ml +++ b/src/version.ml @@ -1,2 +1,2 @@ -let n="0.31" -let d="Mon 15 Jun 12:00:29 BST 2020" +let n="0.32" +let d="Wed 09 Mar 2022 15:44:32 GMT" From 1b4f322396f3e253a51c3430f20314d34b745984 Mon Sep 17 00:00:00 2001 From: pes20 Date: Wed, 9 Mar 2022 16:11:53 +0000 Subject: [PATCH 30/46] rebuild manual --- built_doc/top2.html | 700 +++++++++++++++++++++++++----------------- built_doc/top2.pdf | Bin 519977 -> 536499 bytes built_doc/top2.ps | Bin 2353960 -> 2371792 bytes built_doc/top2001.png | Bin 7699 -> 7699 bytes built_doc/top2002.png | Bin 3476 -> 3476 bytes built_doc/top2003.png | Bin 3443 -> 3443 bytes built_doc/top2004.png | Bin 3165 -> 3165 bytes built_doc/top2005.png | Bin 4877 -> 4877 bytes built_doc/top2006.png | Bin 3976 -> 3976 bytes built_doc/top2007.png | Bin 23538 -> 23538 bytes built_doc/top2008.png | Bin 3589 -> 3589 bytes built_doc/top2009.png | Bin 3415 -> 3415 bytes built_doc/top2010.png | Bin 3075 -> 3075 bytes built_doc/top2011.png | Bin 3024 -> 3024 bytes built_doc/top2012.png | Bin 3778 -> 3778 bytes built_doc/top2013.png | Bin 3284 -> 3284 bytes built_doc/top2014.png | Bin 3036 -> 3036 bytes built_doc/top2015.png | Bin 3036 -> 3036 bytes built_doc/top2016.png | Bin 5668 -> 5668 bytes built_doc/top2017.png | Bin 3036 -> 3036 bytes built_doc/top2018.png | Bin 3066 -> 3066 bytes built_doc/top2019.png | Bin 12582 -> 12582 bytes built_doc/top2020.png | Bin 3865 -> 3865 bytes built_doc/top2021.png | Bin 3130 -> 3130 bytes built_doc/top2022.png | Bin 3160 -> 3160 bytes built_doc/top2023.png | Bin 8680 -> 8680 bytes built_doc/top2024.png | Bin 6949 -> 6949 bytes built_doc/top2025.png | Bin 4236 -> 4236 bytes built_doc/top2026.png | Bin 3880 -> 3880 bytes built_doc/top2027.png | Bin 4814 -> 4814 bytes built_doc/top2028.png | Bin 5140 -> 5140 bytes built_doc/top2029.png | Bin 6601 -> 6601 bytes built_doc/top2030.png | Bin 5630 -> 5630 bytes built_doc/top2031.png | Bin 3261 -> 3261 bytes built_doc/top2032.png | Bin 3039 -> 3039 bytes built_doc/top2033.png | Bin 3059 -> 3059 bytes built_doc/top2034.png | Bin 4325 -> 4325 bytes built_doc/top2035.png | Bin 4734 -> 4734 bytes built_doc/top2036.png | Bin 3881 -> 3881 bytes built_doc/top2037.png | Bin 5819 -> 5819 bytes src/tmp_date.txt | 2 +- src/version.ml | 2 +- 42 files changed, 426 insertions(+), 278 deletions(-) diff --git a/built_doc/top2.html b/built_doc/top2.html index 3351f3f..760d0e5 100644 --- a/built_doc/top2.html +++ b/built_doc/top2.html @@ -52,7 +52,7 @@ Ott: Tool Support for Semantics User Guide - version 0.31 + version 0.32 @@ -61,7 +61,7 @@

Ott: Tool Support for Semantics
User Guide
- version 0.31 + version 0.32

Peter Sewell*     Francesco Zappa Nardelli**     Scott Owens *
with Gilles Peskine*, Tom Ridge*,
@@ -72,101 +72,106 @@

Contents

1  Introduction
  • 2  Getting started with Ott (the README) -
  • 3  A minimal Ott source file: the untyped CBV lambda calculus +
  • 3  A minimal Ott source file: the untyped CBV lambda calculus -
  • 4  Generating LaTeX +
  • 4  Generating LaTeX -
  • 5  Generating proof assistant definitions +
  • 5  Generating proof assistant definitions -
  • 6  Judgments and formulae +
  • 6  Judgments and formulae -
  • 7  Concrete terms and OCaml generation -
  • 8  Filtering: Using Ott syntax within LaTeX, Coq, Isabelle, +
  • 7  Concrete terms and OCaml generation +
  • 8  Filtering: Using Ott syntax within LaTeX, Coq, Isabelle, HOL, or OCaml -
  • 9  Binding specifications -
  • 10  Generating substitution and free variable functions -
  • 11  Locally-nameless representation -
  • 12  List forms +
  • 9  Binding specifications +
  • 10  Generating substitution and free variable functions +
  • 11  Locally-nameless representation +
  • 12  List forms -
  • 13  Subrules -
  • 14  Context rules -
  • 15  Auxiliary Rules -
  • 16  Functions -
  • 17  Parsing Priorities -
  • 18  Combining multiple source files -
  • 19  Hom blocks -
  • 20  Isabelle syntax support -
  • 21  Isabelle code generation example -
  • 22  Reference: Command-line usage -
  • 23  Reference: The language of symbolic terms -
  • 24  Reference: Generation of proof assistant definitions +
  • 13  Subrules +
  • 14  Context rules +
  • 15  Auxiliary Rules +
  • 16  Functions +
  • 17  Parsing Priorities +
  • 18  Combining multiple source files +
  • 19  Hom blocks +
  • 20  Isabelle syntax support +
  • 21  Isabelle code generation example +
  • 22  Reference: Command-line usage +
  • 23  Reference: The language of symbolic terms +
  • 24  Reference: Generation of proof assistant definitions -
  • 25  Reference: Summary of homomorphisms -
  • 26  Reference: The Ott source grammar -
  • 27  Reference: Examples +
  • 25  Reference: Summary of homomorphisms +
  • 26  Reference: The Ott source grammar +
  • 27  Reference: Examples
  • 1  Introduction

    Ott is a tool for writing definitions of programming languages and @@ -190,9 +195,9 @@

    1  Introduction

    +ICFP’07 [SZNO+07].

    give an overview of the project, including discussion of motivation, design decisions, and related work, and one should look at that together with this manual. The project web page @@ -202,11 +207,11 @@

    1  Introduction

    ], the -POPLmark F<: language [], a module system by -Leroy [, §4] (extended with a term language and an +first-order systems from Pierce’s TAPL [Pie02], the +POPLmark F<: language [ABF+05], a module system by +Leroy [Ler96, §4] (extended with a term language and an operational semantics), the LJ Java fragment and LJAM Java module -system [], and a substantial fragment of OCaml.

    Our main goal is to support work on large programming language +system [SSP07], and a substantial fragment of OCaml.

    Our main goal is to support work on large programming language definitions, where the scale makes it hard to keep a definition internally consistent, and hard to keep a tight correspondence between a definition and implementations. @@ -244,65 +249,121 @@

    1  Introduction

    -

    2  Getting started with Ott (the README)

    A tool for writing definitions of programming languages and calculi

    by Peter Sewell, Francesco Zappa Nardelli, and Scott Owens.

    - -

    2.1  Repository and Package

    Ott is now available from -github, and as an opam package.

    We no longer provide non-github tarballs or a Windows distribution.

    - -

    2.2  Directory contents

    - - -


    - - - - - - - - - - - - - - - - - - - - -
    -directorydescription
     
    aux/auxiliary code (y2l) used to build the user -guide
    bin/the Ott binary
    built_doc/the user guide, in html, pdf, and -ps
    coq/auxiliary files for Coq
    doc/the user guide sources
    emacs/an Ott Emacs mode
    examples/some larger example Ott files
    tex/auxiliary files for LaTeX
    hol/auxiliary files for HOL
    menhir/auxiliary files for menhir
    ocamlgraph-1.7.tar.gza copy of the ocamlgraph -library
    regression/regression-test machinery
    tests/various small example Ott files
    src/the (OCaml) Ott sources
    Makefilea Makefile for the examples
    LICENCEthe BSD-style licence terms
    README.mdthis file (Section 2 of the user -guide)
    revisionhistory.txtthe revision history
     
    - -

    - -

    2.3  To build

    - -

    2.3.1  With OPAM

    If you have OPAM installed on your -system, opam install ott will install the latest Ott version. +

    2  Getting started with Ott (the README)

    Ott is a tool for writing definitions of programming languages and +calculi. It takes as input a definition of a language syntax and +semantics, in a concise and readable ASCII notation that is close to +what one would write in informal mathematics. With appropriate +annotations, it can then generate output:

    • +a LaTeX source file that defines commands to build a typeset version +of the definition; +
    • a Coq version of the definition; +
    • a HOL version of the definition; +
    • an Isabelle/HOL version of the definition; +
    • a Lem version of the definition; +
    • an OCaml version of the syntax of the definition; and +
    • (experimental) a menhir parser and crude pretty printer for the +syntax. +

    Additionally, it can be run as a filter, taking a +LaTeX/Coq/Isabelle/HOL/Lem/OCaml source file with embedded (symbolic) +terms of the defined language, parsing them and replacing them by +typeset terms.

    Most simply, Ott can be used to aid informal LaTeX mathematics. Here it +permits the definition, and terms within proofs and exposition, to be +written in a clear, editable, ASCII notation, without LaTeX noise. It +generates good-quality typeset output. By parsing (and so sort-checking) +this input, it quickly catches a range of simple errors, +e.g. inconsistent use of judgement forms or metavariable naming +conventions.

    That same input can be used to generate formal definitions, for Coq, +HOL, Isabelle, and Lem. It should thereby enable a smooth transition +between use of informal and formal mathematics. Additionally, the tool +can automatically generate definitions of functions for free variables, +single and multiple substitutions, subgrammar checks (e.g. for value +subgrammars), and binding auxiliary functions. At present only a fully +concrete representation of binding, without quotienting by alpha +equivalence, is fully supported. An experimental backend generates a +locally-nameless representation of terms for a subset of the Ott +metalanguage: details can be found at +http://moscova.inria.fr/~zappa/projects/ln_ott.

    The distribution includes several examples, in varying levels of +completeness: untyped and simply typed lambda-calculus, a calculus with +ML polymorphism, the POPLmark Fsub with and without records, an ML +module system taken from (Leroy, JFP 1996) and equipped with an +operational semantics, and LJ, a lightweight Java fragment. More +substantially, Ott has been used for work on iJAM and LJAM, Java Module +Systems, by Rok Strnisa, and semantics for OCaml light, by Scott Owens.

    As of 2020, Ott remains in continuous use.

    + +

    2.1  Documentation

    + +

    2.2  Papers

    + +

    2.3  People

    Ott has been principally devloped by Peter Sewell, Francesco Zappa +Nardelli, and Scott Owens, with contributions from many others including +Joey Eremondi, Hannes Mehnert, Karl Palmskog, Matthew Parkinson, Thibaut +Perami, Gilles Peskine, Alastair Reid, Tom Ridge, Susmit Sarkar, Rok +Strnisa, Viktor Vafeiadis.

    + +

    2.4  To install and build

    Ott is available as an opam package and a +github repo.

    + +

    2.4.1  With OPAM (released +version)

    First, ensure you have opam (the OCaml package manager) installed, +version 2.0 or greater (opam 1 versions of ott are no longer supported). +You can use your system’s package manager e.g. +sudo apt-get install opam (e.g. on Ubuntu 20.04) or follow +the instructions from the +opam website. On older Ubuntu versions you will not be able to use +their package manager’s opam 1 version, and will need to install opam 2 +following the instructions on the opam website.

    Then opam install ott will install the latest Ott version. The Emacs mode will be in $(opam config var prefix)/share/emacs/site-lisp, and -documentation in $(opam config var prefix)/doc/ott.

    To install the Ott auxiliary files for Coq, first activate the -coq-released OPAM repository:

    opam repo add coq-released https://coq.inria.fr/opam/released

    and then run opam install coq-ott.

    - -

    2.3.2  Without OPAM

    Ott depends on OCaml version 4.00.0 or later. It builds with (at least) -OCaml 4.02.3.

    The command make (make world) builds the ott +documentation in $(opam config var prefix)/doc/ott.

    If you want to use Ott with the Coq proof assistant, to install the Ott +auxiliary files for Coq, first activate the coq-released OPAM +repository:

    opam repo add coq-released https://coq.inria.fr/opam/released

    and then run opam install coq-ott.

    + +

    2.4.2  With OPAM (github +checkout)

    In the checkout directory, run opam pin add ott ..

    To rebuild and reinstall after local changes, run +opam upgrade --working-dir ott (or +opam upgrade -w ott).

    + +

    2.4.3  Without OPAM

    Ott depends on OCaml version 4.00.0 or later. It builds with (at least) +OCaml 4.02.3 and 4.10.0.

    The command make (make world) builds the ott binary in the bin/ subdirectory.

    This will compile Ott using ocamlopt. To force it to compile with ocamlc (which may give significantly slower execution of Ott), do make world.byt.

    To build the Ott auxiliary files for Coq, go to the coq/ subdirectory and run make. To install the resulting files in Coq’s user-contrib, run make install.

    - -

    2.4  To run

    Ott runs as a command-line tool. Executing bin/ott shows the -usage and options. To run Ott on the test file -tests/test10.ott, generating LaTeX in test10.tex and -Coq in test10.v, type:

    bin/ott -i tests/test10.ott -o test10.tex -o test10.v

    Isabelle, HOL, and Lem can be generated with options + +

    2.5  To run

    Ott runs as a command-line tool. Executing ott shows the usage +and options. To run Ott on the test file tests/test10.ott, +generating LaTeX in test10.tex and Coq in test10.v, +type:

    ott -i tests/test10.ott -o test10.tex -o test10.v

    Isabelle, HOL, and Lem can be generated with options -o test10.thy, -o test10Script.sml, and -o test10.lem, respectively.

    The Makefile has various sample targets, make tests/test10.out, make test7, etc. Typically @@ -322,16 +383,10 @@

    2.4  To run


    from files test10.ott, test8.ott, etc., in tests/.

    - -

    2.5  Manual

    - -

    2.6  Editor Plugins

    - -

    2.6.1  Emacs mode

    The file emacs/ott-mode.el defines a very simple Emacs mode for + +

    2.6  Editor Plugins

    + +

    2.6.1  Emacs mode

    The file emacs/ott-mode.el defines a very simple Emacs mode for syntax highlighting of Ott source files. It can be used by, for example, adding the following to your .emacs file, replacing PATH by a path to your Ott Emacs directory.

    (setq load-path (cons (expand-file-name "PATH") load-path))
    @@ -342,30 +397,85 @@ 

    2.6.1  Emacs mode

    -

    2.6.2  Visual Studio Code

    There is a + +

    2.6.2  Visual Studio Code

    There is a plugin for VSCode, which features syntax highlighting and inline error reporting.

    - -

    2.7  Mailing lists

    • + +

      2.7  Mailing lists

      - -

      2.8  Web page with examples

      • -here + +

        2.8  Bug Tracker

        • +Please now use the github issue tracker (though our resources for +fixing issues are very limited) +
        • The previous issue tracker is here
        - -

        2.9  Copyright information

        The ocamlgraph library is distributed under the LGPL (from + +

        2.9  Directory contents

        + + +


        + + + + + + + + + + + + + + + + + + + + +
        +directorydescription
         
        aux/auxiliary code (y2l) used to build the user +guide
        bin/the Ott binary
        built_doc/the user guide, in html, pdf, and +ps
        coq/auxiliary files for Coq
        doc/the user guide sources
        emacs/an Ott Emacs mode
        examples/some larger example Ott files
        tex/auxiliary files for LaTeX
        hol/auxiliary files for HOL
        menhir/auxiliary files for menhir
        ocamlgraph-1.7.tar.gza copy of the ocamlgraph +library
        regression/regression-test machinery
        tests/various small example Ott files
        src/the (OCaml) Ott sources
        Makefilea Makefile for the examples
        LICENCEthe BSD-style licence terms
        README.mdthis file (Section 2 of the user +guide)
        revisionhistory.txtthe revision history
         
        + +

        + +

        2.10  Examples

        The following LaTeX, Coq, HOL, and Isabelle files, except the proof +scripts, are all automatically generated from the Ott sources.

          <th rowspan="2">System</th>
        +  <th rowspan="2">Rules</th>
        +  <th rowspan="2">Ott sources</th>
        +  <th rowspan="2">Latex</th>
        +  <th rowspan="2">Typeset</th>
        +  <th rowspan="2">Dot</th>
        +  <th colspan="2">Coq</th>
        +  <th colspan="2">HOL</th>
        +  <th colspan="2">Isabelle</th>
        +
          <th> Defn </th>
        +  <th> Proof </th>
        +  <th> Defn </th>
        +  <th> Proof </th>
        +  <th> Defn </th>
        +  <th> Proof </th>
        +  </tr>
        +

        Untyped CBV lambda

        3

        test10.ott

        test10.tex

        (ps)

        test10.v

        test10Script.sml

        test10.thy

        Simply typed CBV lambda

        6

        test10st.ott

        test10st.tex

        (ps)

        test10st.v

        test10st_metatheory.v

        test10stScript.sml

        test10st_metatheoryScript.sml

        test10st.thy

        test10st_metatheory.thy

        ML polymorphism

        22

        test8.ott

        test8.tex

        (ps)

        test8.v

        test8Script.sml

        test8.thy

        TAPL roughly-full-simple

        63

        (sources)

        (ps)

        (Coq (including records))

        (HOL)

        (script)

        (Isabelle)

        (script)

        POPLmark Fsub (*)

        48

        (sources)

        (latex)

        (pdf)   (ps)

        Leroy JFP96 module system (*)

        67

        leroy-jfp96.ott

        (latex)

        (ps)

        (HOL)

        LJ: Lightweight Java

        85

        (sources)

        (pdf)

        (Isabelle)

        (zip)

        LJAM: Java Module System

        163

        (sources)

        (pdf)

        (Isabelle)

        (zip)

        OCaml light

        310

        (sources)

        (ps)

        (ps)

        (Coq)

        (HOL)

        (scripts)

        (Isabelle)

        (*) These systems would need explicit alpha conversion in the rules to +capture the intended semantics using the fully concrete representation.

        + +

        2.11  Copyright information

        The ocamlgraph library is distributed under the LGPL (from http://www.lri.fr/~filliatr/ftp/ocamlgraph/); we include a snapshot for convenience. For its authorship and copyright information see the files therein.

        All other files are distributed under the BSD-style licence in LICENCE.

        - -

        3  A minimal Ott source file: the untyped CBV lambda calculus

        Fig. 1 shows an Ott source file for an untyped call-by-value + +

        3  A minimal Ott source file: the untyped CBV lambda calculus

        Fig. 1 shows an Ott source file for an untyped call-by-value (CBV) lambda calculus. This section explains the basic features that appear there, while in the following sections we show what must be added to generate typeset output, proof assistant definitions, and @@ -452,8 +562,8 @@

        3  A minimal Ott source file: the untyp and S are identical except that productions with the latter are admitted when parsing example concrete terms; the S tag is thus appropriate for lightweight syntactic sugar, such as productions for -parentheses. One can also use an X flag here to suppress a -production in the generated LaTeX.

        Each production has a production name (e.g. t_Lam), composed of +parentheses. One can also use another flag, e.g. X, along with the command-line option -tex_suppress_category, to suppress +productions in the generated LaTeX.

        Each production has a production name (e.g. t_Lam), composed of the rule name prefix (here t_) and the production name kernel that follows the ::’s (here Lam). The production name is used as a constructor name in the generated Isabelle/Coq/HOL.

        The tool supports arbitrary context-free grammars, extended with @@ -551,8 +661,8 @@

        3  A minimal Ott source file: the untyp your screen add -colour false to the middle of the command line. To suppress the echo of the definition, add -show_sort false and -show_defns false.

        - -

        3.1  Index variables

        + +

        3.1  Index variables

        In addition to the metavar declarations above, the user can declare any number of distinguished index metavariables, e.g. by:

        @@ -565,8 +675,8 @@ 

        3.1  Index variables

        -

        4  Generating LaTeX

        The example from the previous section can already be used to generate + +

        4  Generating LaTeX

        The example from the previous section can already be used to generate LaTeX, for example by executing

            bin/ott -i tests/test10.0.ott -o out.tex 
        @@ -664,8 +774,8 @@ 

        4  Generating LaTeX

        {{ com [[t1]] reduces to [[t2]]}} attached to a semantic relation. These appear in the LaTeX output as shown in Figure 3.

        - -

        4.1  Specifying LaTeX for productions

        + +

        4.1  Specifying LaTeX for productions

        One can also specify tex translations for productions, overriding the default LaTeX typesetting, e.g. as in this example of a type abstraction production. @@ -706,8 +816,8 @@

        4.1  Specifying LaTeX for production pretty-printing defaults to a sequence of the individual items separated by thin spaces (\,), with reasonable default fonts and making use of the terminals grammar where appropriate.

        - -

        4.2  Specifying LaTeX for grammar rules

        + +

        4.2  Specifying LaTeX for grammar rules

        Grammar rules can include a tex hom specifying how all the nonterminal roots should be typeset, e.g.

        @@ -726,8 +836,8 @@ 

        4.2  Specifying LaTeX for grammar ru terms, to be typeset as etc.

        - -

        4.3  Using the LaTeX code

        The generated LaTeX code can be used in two main ways. + +

        4.3  Using the LaTeX code

        The generated LaTeX code can be used in two main ways. By default, Ott generates a stand-alone LaTeX file, with a standard wrapper (including a \documentclass, various macro definitions, and a main body), @@ -771,8 +881,8 @@

        4.3  Using the LaTeX code

        -

        5  Generating proof assistant definitions

        To generate proof assistant definitions, for Coq, Isabelle, and HOL, + +

        5  Generating proof assistant definitions

        To generate proof assistant definitions, for Coq, Isabelle, and HOL, the minimal Ott source file of Section 3/Figure 1 must be extended with a modest amount of additional data, as shown in Figure 4. Executing @@ -894,7 +1004,7 @@

        5  Generating proof assistant definitio parentheses, as in the Tsub hom above, so that it is parsed correctly by the proof assistant in all contexts.


        -
        (* generated by Ott 0.31 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
        +
        (* generated by Ott 0.32 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
         
         Require Import Arith.
         Require Import Bool.
        @@ -961,7 +1071,7 @@ 

        5  Generating proof assistant definitio



    -
    (* generated by Ott 0.31 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
    +
    (* generated by Ott 0.32 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
     theory test10
     imports Main
     begin
    @@ -1016,7 +1126,7 @@ 

    5  Generating proof assistant definitio



    -
    (* generated by Ott 0.31 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
    +
    (* generated by Ott 0.32 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
     (* to compile: Holmake test10Theory.uo   *)
     (* for interactive use:
       app load ["pred_setTheory","finite_mapTheory","stringTheory","containerTheory","ottLib"];
    @@ -1084,8 +1194,8 @@ 

    5  Generating proof assistant definitio
    Figure 7: Generated HOL:test10Script.sml

    - -

    5.1  Proof assistant code for grammar rules

    + +

    5.1  Proof assistant code for grammar rules

    The normal behaviour is to generate a free proof assistant type for each (non-subrule, non-phantom) grammar rule. For example, the Coq compilation for t here generates a free type with three @@ -1151,8 +1261,8 @@

    5.1  Proof assistant code for gramma When generating Isabelle/Coq/HOL/OCaml representation types, however, they are topologically sorted, to simplify the resulting induction principles.

    - -

    5.2  Proof assistant code for inductive definitions

    + +

    5.2  Proof assistant code for inductive definitions

    The semantic relations are defined with the proof-assistant inductive relations packages, Inductive, Hol_reln, and inductive_set or inductive, respectively. @@ -1185,14 +1295,14 @@

    5.2  Proof assistant code for induct For Coq and HOL, explicit quantifiers are introduced for all variables mentioned in the rule. For HOL, rules are tagged with their rule name (using clause_name).

    - -

    5.3  Representation of binding

    + +

    5.3  Representation of binding

    At present the generated Isabelle/Coq/HOL uses fully concrete representations of variables in terms, without any notion of alpha equivalence, as one can see in Fig. 6: see the t datatype of terms and the tsubst_t substitution function there. -An experimental Coq backend generates definitions in locally-nameless style for a subset of the Ott metalanguage. This is work-in-progress, and it is extensively documented in http://moscova.inria.fr/ zappa/projects/ln_ott/. +An experimental Coq backend generates definitions in locally-nameless style for a subset of the Ott metalanguage. This is work-in-progress, and it is extensively documented in http://moscova.inria.fr/~zappa/projects/ln_ott/. We intend in future to generate other representations, and in some circumstances homs can be used to implement other representations directly. For a reasonably wide variety of @@ -1206,14 +1316,14 @@

    5.3  Representation of binding

    < environment with internal dependencies, however, for example F<:, this is no longer the case. The POPLmark F<: example given in test7.ott has a type system which disallows all shadowing, a property that is -not preserved by reduction. However, a correct translation of F<: is generated by the Coq locally-nameless backend, and can be found in http://moscova.inria.fr/ zappa/projects/ln_ott/.

    Further discussion of binding representations is in the Ott ICFP 2007 +not preserved by reduction. However, a correct translation of F<: is generated by the Coq locally-nameless backend, and can be found in http://moscova.inria.fr/~zappa/projects/ln_ott/.

    Further discussion of binding representations is in the Ott ICFP 2007 paper and in a working draft

    Binding and Substitition. Susmit Sarkar, Peter Sewell, and Francesco Zappa Nardelli. August 2007.

    available from the Ott web page.

    - -

    5.4  Helper functions for free variable and substitution functions

    The generated free variable and substitution functions in the Coq output + +

    5.4  Helper functions for free variable and substitution functions

    The generated free variable and substitution functions in the Coq output (e.g., in Figure 5) often rely on a few standard library functions: list_mem, list_assoc, list_minus, list_minus2. In order to avoid dependencies on external libraries for defining those @@ -1229,8 +1339,8 @@

    5.4  Helper functions for free varia difficult to reason about. For backwards compatibility, however, we provide the command-line option -coq_use_filter_fn for generating a definition using the older code pattern.

    - -

    5.5  Correctness of the generated proof assistant code

    + +

    5.5  Correctness of the generated proof assistant code

    We have attempted to ensure that the proof assistant definitions generated by Ott are well-formed and what the user would intend. This is not guaranteed, however, for several reasons: (1) There may be name @@ -1246,8 +1356,8 @@

    5.5  Correctness of the generated pr are as intended — but typically one would do this in any case, many times over, in the process of proving metatheoretic results, so we do not consider it a major issue.

    - -

    5.6  Using the generated proof assistant code

    + +

    5.6  Using the generated proof assistant code

    Note added 2017-11-30: the following is out of date.

    Ott builds code for

    Coq 8.3http://coq.inria.fr/
    HOL 4 (the current svn version)http://hol.sourceforge.net/
    Isabelle/HOL (Isabelle 2011)http://isabelle.in.tum.de/ @@ -1256,8 +1366,8 @@

    5.6  Using the generated proof assis distribution, as produced at the start of this section (Coq out.v, Isabelle out.thy, and HOL outScript.sml), the various proof assistants can be invoked as follows.

    - -

    5.6.1  Coq

    + +

    5.6.1  Coq

    First run

      make
     

    @@ -1271,8 +1381,8 @@

    5.6.1  Coq

      coqc -I coq  out.v
     

    where coq is the path to the coq directory of the distribution.

    The experimental locally-nameless backend requires the Metatheory library by Arthur Chargueraud, available from the project web page.

    - -

    5.6.2  HOL

    + +

    5.6.2  HOL

    First run

      Holmake
     

    @@ -1289,16 +1399,16 @@

    5.6.2  HOL

    another window view the outScript.sml file. First paste in the app load command from a comment at the top of the file, then paste in the remainder.

    - -

    5.6.3  Isabelle

    + +

    5.6.3  Isabelle

    For batch mode:

      echo 'ML_command {* (use_thy "Tmp"; OS.Process.exit OS.Process.success) handle e => (OS.Process.exit OS.Process.failure); *}' | /usr/local/Isabelle/bin/isabelle tty 
     

    Interactively, using Proof General:

      isabelle emacs out.thy
     
    - -

    6  Judgments and formulae

    In a semantic rule, for example + +

    6  Judgments and formulae

    In a semantic rule, for example

         t1 --> t1’
         -------------- :: ctx_app_arg
    @@ -1338,8 +1448,8 @@ 

    6  Judgments and formulae

    -

    6.1  Naming of premises for the Coq backend

    + +

    6.1  Naming of premises for the Coq backend

    It is possible to specify the names of premises of inductive predicates; these names are then used by the Coq backend, and are often useful in proofs. For instance, we can call RED the hypothesis in the rule below @@ -1359,8 +1469,8 @@

    6.1  Naming of premises for the Coq characters, and must begin with a letter. The name annotation must at the rightmost place on the hypothesis line, and must be enclosed (without spaces) between the [[: and ]] parentheses.

    - -

    6.2  In-line embedded prover code in premises

    + +

    6.2  In-line embedded prover code in premises

    Instead of adding a formula production, one can directly embed prover code as a premise, delimited as below by {{ and }}. Within that, text will be echoed @@ -1372,13 +1482,13 @@

    6.2  In-line embedded prover code in ----------------------------------------------------------- :: Assign1 e1=e2 . k |env --tau--> lval(e1) . [__=c e2] . k |env

    - -

    6.3  User syntax

    + +

    6.3  User syntax

    The tool also synthesises a user_syntax grammar of all the user syntax, for example:

    This is used for parsing top-level strings, for example when filtering embedded code (§8).

    - -

    7  Concrete terms and OCaml generation

    In semantic definitions, one typically never uses concrete variables, + +

    7  Concrete terms and OCaml generation

    In semantic definitions, one typically never uses concrete variables, only metavariables that range over them. In examples, however, one may need either a mix of concrete variables and metavariables, or, for strictly concrete terms, to restrict to @@ -1468,7 +1578,7 @@

    7  Concrete terms and OCaml generation<



    -
    (* generated by Ott 0.31 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
    +
    (* generated by Ott 0.32 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
     
     type var = int (* term variable *)
     
    @@ -1504,12 +1614,12 @@ 

    7  Concrete terms and OCaml generation<
    Figure 9: Generated OCaml code: test10.ml

    - -

    8  Filtering: Using Ott syntax within LaTeX, Coq, Isabelle, +

    8  Filtering: Using Ott syntax within LaTeX, Coq, Isabelle, HOL, or OCaml

    - -

    8.1  Filtering embedded code

    It is possible to embed arbitrary code in + +

    8.1  Filtering embedded code

    It is possible to embed arbitrary code in the Ott source using an embed block, which can contain tex, coq, @@ -1564,8 +1674,8 @@

    8.1  Filtering embedded code

    ATEX commands must appear in such a tex-preamble section.

    - -

    8.2  Filtering files

    + +

    8.2  Filtering files

    Similar processing can be carried out on separate files, using the command-line options tex_filter, isa_filter, etc. Each of these takes two arguments, a source filename and a destination @@ -1650,8 +1760,8 @@

    8.2  Filtering files

    -

    9  Binding specifications

    Our first example involved a production with a single binder: + +

    9  Binding specifications

    Our first example involved a production with a single binder:

    specified by the source shown in Figure 4:

    @@ -1719,9 +1829,9 @@ 

    9  Binding specifications

    -

    10  Generating substitution and free variable functions

    The tool can generate Isabelle/Coq/HOL/OCaml code for both single and multiple +calculus definitions [FGL+96], dependent record patterns, and many others.

    + +

    10  Generating substitution and free variable functions

    The tool can generate Isabelle/Coq/HOL/OCaml code for both single and multiple substitution functions. For example, the ML polymorphism Ott source of test8.ott includes the following.

    @@ -1769,8 +1879,8 @@ 

    10  Generating substitution and free va ftv_typscheme :: "typscheme => typvar list" ftv_G :: "G => typvar list"

    - -

    11  Locally-nameless representation

    The Coq backend of Ott includes experimental support for a + +

    11  Locally-nameless representation

    The Coq backend of Ott includes experimental support for a locally-nameless representation (and co-finite quantification).

    The user must specify which metavariables require a locally-nameless representation via the repr-locally-nameless hom, e.g.:

    @@ -1870,9 +1980,9 @@ 

    11  Locally-nameless representation

    Current limitations: support for single binders only, no auxfn, Coq only.

    Disclaimer: to compile rule definitions, Ott applies blindly the algorithm described above. Although in most of the cases, this generates a correct and idiomatic representation of the language, some language constructs might not be faithfully translated. Please, let us know if you find one of these cases.

    If Ott is invoked with the -coq_lngen option, then the generated locally-nameless Coq code is compatible with Aydemir’s -lngen tool (http://www.cis.upenn.edu/ baydemir/papers/lngen/).

    - -

    12  List forms

    Ott has direct support for lists, both as dot forms such as +lngen tool (http://www.cis.upenn.edu/ sweirich/papers/lngen/).

    + +

    12  List forms

    Ott has direct support for lists, both as dot forms such as and as list comprehensions such as @@ -1905,8 +2015,8 @@

    12  List forms

    Figure 13: A sample OCaml semantic rule, in LaTeX and Ott source forms

    - -

    12.1  List dot forms

    Example productions for + +

    12.1  List dot forms

    Example productions for record types, record terms, and record patterns are shown below, in both Ott source and LaTeX, taken from our F<: example. @@ -1960,8 +2070,8 @@

    12.1  List dot forms

    -

    12.2  List comprehension forms

    Lists can also be expressed as explicit list comprehensions, + +

    12.2  List comprehension forms

    Lists can also be expressed as explicit list comprehensions, for more concise typesetting. Three different styles are supported, with no bounds, an upper bound, or a lower and upper bound. For example, in a symbolic @@ -2054,10 +2164,10 @@

    12.2  List comprehension forms

    < should be automated in future Ott releases, though it would bring the typeset and ASCII versions out of step.

    List comprehension forms can also be used in bindspecs and in homomorphisms.

    - -

    12.3  Proof assistant code for list forms

    - -

    12.3.1  Types

    + +

    12.3  Proof assistant code for list forms

    + +

    12.3.1  Types

    We have to choose proof assistant representations for productions involving list forms. For example, for a language with records one might write

    @@ -2098,8 +2208,8 @@ 

    12.3.1  Types

    These are included in the grammar topological sort, and utility functions, e.g. to make and unmake lists, are synthesised.

    - -

    12.3.2  Terms (in inductive definition rules)

    + +

    12.3.2  Terms (in inductive definition rules)

    Supporting list forms in the rules of an inductive definition requires some additional analysis. For example, consider the record typing rule below.

    We analyse the symbolic terms in the premises and conclusion to @@ -2185,8 +2295,8 @@

    12.3.2  Terms (in inductive defin The generated code for substitutions and free variables takes account of such list structure.

    Note that at present the generated Isabelle code for these functions does not always build without change, in particular if tuples of size 3 or more are required in patterns.

    - -

    12.3.3  List forms in homomorphisms

    Proof assistant homomorphisms in productions can refer to dot-form + +

    12.3.3  List forms in homomorphisms

    Proof assistant homomorphisms in productions can refer to dot-form metavariables and nonterminals. For example, the second production below (taken from test17.9) mentions [[x1 t1 ... xn tn]] in the isa homomorphism. This must exactly match the dot form in the production @@ -2231,8 +2341,8 @@

    12.3.3  List forms in homomorphis Note that in the second the list of pairs is projected out from the x_t_K_list list of triples that is quantified over in the rule.

    - -

    13  Subrules

    Subrule declarations have the form + +

    13  Subrules

    Subrule declarations have the form

       subrules
         nt1 <:: nt2
    @@ -2282,8 +2392,8 @@ 

    13  Subrules

    ax uses both predicates. The Isabelle clause is below.

      axI: "[|is_t t ; is_v v|] ==>  ( t , v ) : Baz"
     
    - -

    14  Context rules

    The system supports the definition of single-hole contexts, e.g. for + +

    14  Context rules

    The system supports the definition of single-hole contexts, e.g. for evaluation contexts. For example, suppose one has a term grammar as below:

     t :: ’t_’ ::=                               {{ com term    }}
    @@ -2362,8 +2472,8 @@ 

    14  Context rules

    terminals :: ’terminals_’ ::= | __ :: :: hole {{ tex [\cdot] }}

    - -

    15  Auxiliary Rules

    + +

    15  Auxiliary Rules

    We permit an aux hom on grammar rules. For any rule with such a hom, we transform that rule by appending an _aux to its primary nonterminal root name. We then add a synthesised rule with the original nonterminal @@ -2390,8 +2500,8 @@

    15  Auxiliary Rules

    parameters to the generated OCaml output.

    Generation of aux rules is controlled by a command-line option -generate_aux_rules, which one might (eg) set to false for latex output and true for OCaml output.

    - -

    16  Functions

    Ott includes experimental support for writing function definitions. + +

    16  Functions

    Ott includes experimental support for writing function definitions. As a simple example, consider the Ott file below:

         grammar
          n :: ’n_’ ::=
    @@ -2435,8 +2545,8 @@ 

    16  Functions

    Ott definitions.

    Disclaimer: the different treatment of partial functions by the different provers can result in a function definition being compiled correctly to one prover but not to others.

    - -

    17  Parsing Priorities

    Symbolic terms that can have more than one parse tree are typically considered + +

    17  Parsing Priorities

    Symbolic terms that can have more than one parse tree are typically considered erroneous; however, certain classes of parse trees are ignored in order to support common idioms that are ambiguous. For example, the production

    @@ -2533,8 +2643,8 @@

    17  Parsing Priorities

    -

    18  Combining multiple source files

    + +

    18  Combining multiple source files

    Ott can be invoked with multiple source files. Input filenames with extensions .tex, .v, .thy, .sml, or ml are simply copied into @@ -2555,7 +2665,7 @@

    18  Combining multiple source files

    15 shows the Ott source file for a let feature in isolation, taken from our Ott development of -some languages from Pierce’s TAPL []. +some languages from Pierce’s TAPL [Pie02].


    
    @@ -2594,14 +2704,14 @@ 

    18  Combining multiple source files


    The original TAPL languages were produced using -TinkerType [] to compose features and check for +TinkerType [LP03] to compose features and check for conflicts. In examples/tapl we build a system, similar to the TinkerType sys-fullsimple, from ott source files that correspond roughly to the various TinkerType components, each with syntax and semantic rules for a single feature.

    - -

    19  Hom blocks

    + +

    19  Hom blocks

    Bindspecs and homomorphisms for productions, and any homomorphisms for definitions, can appear in an Ott source file either attached to the production or definition, as we have shown earlier, or @@ -2694,8 +2804,8 @@

    19  Hom blocks

    Γ ::= Γ1, .., Γn
    Figure 16: Hom Sections: test10_homs.ott

    - -

    20  Isabelle syntax support

    Ott has limited facilities to allow the Isabelle mixfix syntax support + +

    20  Isabelle syntax support

    Ott has limited facilities to allow the Isabelle mixfix syntax support and xsymbol to be used. The example test10_isasyn.ott shows this in use.

    Non-meta productions can be annotated with @@ -2751,8 +2861,8 @@

    20  Isabelle syntax support

    -

    21  Isabelle code generation example

    The Isabelle/Coq/HOL code generation facilities can be sometimes used to + +

    21  Isabelle code generation example

    The Isabelle/Coq/HOL code generation facilities can be sometimes used to generate (variously) OCaml and SML code from the Isabelle/Coq/HOL definitions produced by Ott.

    For example, the test10st_codegen.thy file uses Isabelle code generation to produce SML code to calculate the possible @@ -2799,8 +2909,8 @@

    21  Isabelle code generation example - -

    22  Reference: Command-line usage

    A good place to get started is one of the test + +

    22  Reference: Command-line usage

    A good place to get started is one of the test make targets in the ott directory, e.g.

    test10: tests/test10.ott
                bin/ott                                                \
    @@ -2841,7 +2951,7 @@ 

    22  Reference: Command-line usage

    <  %.isa.out, to generate just LaTeX and the code for one proof assistant, and  %.tex.out, to generate just LaTeX.

    The ott command-line options (with default values where applicable) are shown below. -

    Ott version 0.31   distribution of Mon 15 Jun 12:00:29 BST 2020
    +

    Ott version 0.32   distribution of Wed 9 Mar 16:05:28 GMT 2022
     
     usage: ott <options> <filename1> .. <filenamen> 
       (use "OCAMLRUNPARAM=p  ott ..." to show the ocamlyacc trace)
    @@ -2892,6 +3002,7 @@ 

    22  Reference: Command-line usage

    < -ocaml_include_terminals <false> Include terminals in OCaml output (experimental!) -ocaml_pp <target.ml filename> generate OCaml AST pretty printer files (experimental!) (also included in .mly target) -ocaml_pp_ast_module override default OCaml module name for AST module (experimental!) + -ocaml_pp_json <false> Include JSON output in pretty printer (experimental) -pp_grammar (debug) print term grammar -dot <filename> (debug) dot graph of syntax dependencies -alltt <filename> (debug) alltt output of single source file @@ -2904,8 +3015,8 @@

    22  Reference: Command-line usage

    < -help Display this list of options --help Display this list of options
    - -

    23  Reference: The language of symbolic terms

    A syntax definition conceptually defines two different languages: that + +

    23  Reference: The language of symbolic terms

    A syntax definition conceptually defines two different languages: that of concrete terms of the object language, and that of symbolic terms over the object language. The former includes concrete variables (if nontrivial lex homs @@ -3076,12 +3187,12 @@

    23  Reference: The language of symbolic achieves reasonable performance on the small symbolic terms that are typical in semantic rules. Its performance on large (whole-program size) examples is untested.

    - -

    24  Reference: Generation of proof assistant definitions

    This section briefly summarises the steps involved in the generation + +

    24  Reference: Generation of proof assistant definitions

    This section briefly summarises the steps involved in the generation of proof assistant definitions from an Ott source file. For a description of the locally-nameless backend, refer to http://moscova.inria.fr/ zappa/projects/ln_ott/.

    - -

    24.1  Generation of types

    give an overview of the project, including discussion of motivation, design decisions, and related work, and one should look at that together with this manual. The project web page @@ -207,11 +226,11 @@

    1  Introduction

    Pie02], the -POPLmark F<: language [ABF+05], a module system by -Leroy [Ler96, §4] (extended with a term language and an +first-order systems from Pierce’s TAPL ‍[], the +POPLmark F<: language ‍[], a module system by +Leroy ‍[, §4] (extended with a term language and an operational semantics), the LJ Java fragment and LJAM Java module -system [SSP07], and a substantial fragment of OCaml.

    Our main goal is to support work on large programming language +system ‍[], and a substantial fragment of OCaml.

    Our main goal is to support work on large programming language definitions, where the scale makes it hard to keep a definition internally consistent, and hard to keep a tight correspondence between a definition and implementations. @@ -223,14 +242,14 @@

    1  Introduction

    1  Introduction

    +aim to make this more commonplace, less of a heroic task.

    -

    2  Getting started with Ott (the README)

    Ott is a tool for writing definitions of programming languages and +

    2 Getting started with Ott (the README)

    Ott is a tool for writing definitions of programming languages and calculi. It takes as input a definition of a language syntax and semantics, in a concise and readable ASCII notation that is close to what one would write in informal mathematics. With appropriate @@ -271,12 +290,12 @@

    2  Getting started with Ott (the README) written in a clear, editable, ASCII notation, without LaTeX noise. It generates good-quality typeset output. By parsing (and so sort-checking) this input, it quickly catches a range of simple errors, -e.g. inconsistent use of judgement forms or metavariable naming +e.g. ‍inconsistent use of judgement forms or metavariable naming conventions.

    That same input can be used to generate formal definitions, for Coq, HOL, Isabelle, and Lem. It should thereby enable a smooth transition between use of informal and formal mathematics. Additionally, the tool can automatically generate definitions of functions for free variables, -single and multiple substitutions, subgrammar checks (e.g. for value +single and multiple substitutions, subgrammar checks (e.g. ‍for value subgrammars), and binding auxiliary functions. At present only a fully concrete representation of binding, without quotienting by alpha equivalence, is fully supported. An experimental backend generates a @@ -288,21 +307,21 @@

    2  Getting started with Ott (the README) module system taken from (Leroy, JFP 1996) and equipped with an operational semantics, and LJ, a lightweight Java fragment. More substantially, Ott has been used for work on iJAM and LJAM, Java Module -Systems, by Rok Strnisa, and semantics for OCaml light, by Scott Owens.

    As of 2020, Ott remains in continuous use.

    +Systems, by Rok Strnisa, and semantics for OCaml light, by Scott Owens.

    As of 2020, Ott remains in continuous use.

    -

    2.1  Documentation

    -

    2.2  Papers

    -

    2.3  People

    Ott has been principally devloped by Peter Sewell, Francesco Zappa +

    2.3 People

    Ott has been principally developed by Peter Sewell, Francesco Zappa Nardelli, and Scott Owens, with contributions from many others including Joey Eremondi, Hannes Mehnert, Karl Palmskog, Matthew Parkinson, Thibaut Perami, Gilles Peskine, Alastair Reid, Tom Ridge, Susmit Sarkar, Rok -Strnisa, Viktor Vafeiadis.

    +Strnisa, Viktor Vafeiadis.

    -

    2.4  To install and build

    Ott is available as an opam package and a -github repo.

    +

    2.4 To install and build

    Ott is available as an opam package and a +github repo.

    -

    2.4.1  With OPAM (released -version)

    First, ensure you have opam (the OCaml package manager) installed, +

    2.4.1 With OPAM (released +version)

    First, ensure you have opam (the OCaml package manager) installed, version 2.0 or greater (opam 1 versions of ott are no longer supported). -You can use your system’s package manager e.g. -sudo apt-get install opam (e.g. on Ubuntu 20.04) or follow -the instructions from the -opam website. On older Ubuntu versions you will not be able to use -their package manager’s opam 1 version, and will need to install opam 2 -following the instructions on the opam website.

    Then opam install ott will install the latest Ott version. +You can use your system’s package manager +e.g. ‍sudo apt-get install opam (e.g. ‍on Ubuntu 20.04) or +follow the instructions +from the opam website. On older Ubuntu versions you will not be able to +use their package manager’s opam 1 version, and will need to install +opam 2 following the instructions on the opam website.

    Then opam install ott will install the latest Ott version. The Emacs mode will be in $(opam config var prefix)/share/emacs/site-lisp, and documentation in $(opam config var prefix)/doc/ott.

    If you want to use Ott with the Coq proof assistant, to install the Ott auxiliary files for Coq, first activate the coq-released OPAM -repository:

    opam repo add coq-released https://coq.inria.fr/opam/released

    and then run opam install coq-ott.

    +repository:

    opam repo add coq-released https://coq.inria.fr/opam/released

    and then run opam install coq-ott.

    -

    2.4.2  With OPAM (github -checkout)

    In the checkout directory, run opam pin add ott ..

    To rebuild and reinstall after local changes, run +

    2.4.2 With OPAM (github +checkout)

    In the checkout directory, run opam pin add ott ..

    To rebuild and reinstall after local changes, run opam upgrade --working-dir ott (or -opam upgrade -w ott).

    +opam upgrade -w ott).

    -

    2.4.3  Without OPAM

    Ott depends on OCaml version 4.00.0 or later. It builds with (at least) -OCaml 4.02.3 and 4.10.0.

    The command make (make world) builds the ott +

    2.4.3 Without OPAM

    Ott depends on OCaml version 4.07.0 or later and the ocamlgraph package. +It builds with (at least) OCaml 4.07.0 and 4.14.0, and ocamlgraph 1.8.8.

    The command make (make world) builds the ott binary in the bin/ subdirectory.

    This will compile Ott using ocamlopt. To force it to compile with ocamlc (which may give significantly slower execution of Ott), do make world.byt.

    To build the Ott auxiliary files for Coq, go to the coq/ subdirectory and run make. To install the resulting files in -Coq’s user-contrib, run make install.

    +Coq’s user-contrib, run make install.

    -

    2.5  To run

    Ott runs as a command-line tool. Executing ott shows the usage +

    2.5 To run

    Ott runs as a command-line tool. Executing ott shows the usage and options. To run Ott on the test file tests/test10.ott, generating LaTeX in test10.tex and Coq in test10.v, type:

    ott -i tests/test10.ott -o test10.tex -o test10.v

    Isabelle, HOL, and Lem can be generated with options @@ -370,7 +389,7 @@

    2.5  To run

    -


    +


    @@ -381,12 +400,12 @@

    2.5  To run

    filenamedescription
     
    out.texLaTeX source for a definition
     
    -

    from files test10.ott, test8.ott, etc., in -tests/.

    +

    from files test10.ott, test8.ott, etc., in +tests/.

    -

    2.6  Editor Plugins

    +

    2.6 Editor Plugins

    -

    2.6.1  Emacs mode

    The file emacs/ott-mode.el defines a very simple Emacs mode for +

    2.6.1 Emacs mode

    The file emacs/ott-mode.el defines a very simple Emacs mode for syntax highlighting of Ott source files. It can be used by, for example, adding the following to your .emacs file, replacing PATH by a path to your Ott Emacs directory.

    (setq load-path (cons (expand-file-name "PATH") load-path))
    @@ -396,30 +415,30 @@ 

    2.6.1  Emacs mode

    -

    2.6.2  Visual Studio Code

    There is a +

    2.6.2 Visual Studio Code

    There is a plugin for VSCode, which features syntax highlighting and inline error -reporting.

    +reporting.

    -

    2.7  Mailing lists

    -

    2.8  Bug Tracker

    • +

      2.8 Bug Tracker

      • Please now use the github issue tracker (though our resources for fixing issues are very limited)
      • The previous issue tracker is here -
      +

    -

    2.9  Directory contents

    +

    2.9 Directory contents

    -


    +


    directorydescription
     
    aux/auxiliary code (y2l) used to build the user @@ -447,82 +466,66 @@

    2.9  Directory contents

    -

    2.10  Examples

    The following LaTeX, Coq, HOL, and Isabelle files, except the proof -scripts, are all automatically generated from the Ott sources.

      <th rowspan="2">System</th>
    -  <th rowspan="2">Rules</th>
    -  <th rowspan="2">Ott sources</th>
    -  <th rowspan="2">Latex</th>
    -  <th rowspan="2">Typeset</th>
    -  <th rowspan="2">Dot</th>
    -  <th colspan="2">Coq</th>
    -  <th colspan="2">HOL</th>
    -  <th colspan="2">Isabelle</th>
    -
      <th> Defn </th>
    -  <th> Proof </th>
    -  <th> Defn </th>
    -  <th> Proof </th>
    -  <th> Defn </th>
    -  <th> Proof </th>
    -  </tr>
    -

    Untyped CBV lambda

    3

    test10.ott

    test10.tex

    (ps)

    test10.v

    test10Script.sml

    test10.thy

    Simply typed CBV lambda

    6

    test10st.ott

    test10st.tex

    (ps)

    test10st.v

    test10st_metatheory.v

    test10stScript.sml

    test10st_metatheoryScript.sml

    test10st.thy

    test10st_metatheory.thy

    ML polymorphism

    22

    test8.ott

    test8.tex

    (ps)

    test8.v

    test8Script.sml

    test8.thy

    TAPL roughly-full-simple

    63

    (sources)

    (ps)

    (Coq (including records))

    (HOL)

    (script)

    (Isabelle)

    (script)

    POPLmark Fsub (*)

    48

    (sources)

    (latex)

    (pdf)   (ps)

    Leroy JFP96 module system (*)

    67

    leroy-jfp96.ott

    (latex)

    (ps)

    (HOL)

    LJ: Lightweight Java

    85

    (sources)

    (pdf)

    (Isabelle)

    (zip)

    LJAM: Java Module System

    163

    (sources)

    (pdf)

    (Isabelle)

    (zip)

    OCaml light

    310

    (sources)

    (ps)

    (ps)

    (Coq)

    (HOL)

    (scripts)

    (Isabelle)

    (*) These systems would need explicit alpha conversion in the rules to -capture the intended semantics using the fully concrete representation.

    +

    2.10 Examples

    The following LaTeX, Coq, HOL, and Isabelle files, except the proof +scripts, are all automatically generated from the Ott sources.

    System

    Rules

    Ott sources

    Latex

    Typeset

    Dot

    Coq

    HOL

    Isabelle

    Defn

    Proof

    Defn

    Proof

    Defn

    Proof

    Untyped CBV lambda

    3

    test10.ott

    test10.tex

    (ps)

    test10.v

    test10Script.sml

    test10.thy

    Simply typed CBV lambda

    6

    test10st.ott

    test10st.tex

    (ps)

    test10st.v

    test10st_metatheory.v

    test10stScript.sml

    test10st_metatheoryScript.sml

    test10st.thy

    test10st_metatheory.thy

    ML polymorphism

    22

    test8.ott

    test8.tex

    (ps)

    test8.v

    test8Script.sml

    test8.thy

    TAPL roughly-full-simple

    63

    (sources)

    (ps)

    (Coq (including records))

    (HOL)

    (script)

    (Isabelle)

    (script)

    POPLmark Fsub (*)

    48

    (sources)

    (latex)

    (pdf)  ‍ (ps)

    Leroy JFP96 module system (*)

    67

    leroy-jfp96.ott

    (latex)

    (ps)

    (HOL)

    LJ: Lightweight Java

    85

    (sources)

    (pdf)

    (Isabelle)

    (zip)

    LJAM: Java Module System

    163

    (sources)

    (pdf)

    (Isabelle)

    (zip)

    OCaml light

    310

    (sources)

    (ps)

    (ps)

    (Coq)

    (HOL)

    (scripts)

    (Isabelle)

    (*) These systems would need explicit alpha conversion in the rules to +capture the intended semantics using the fully concrete representation.

    -

    2.11  Copyright information

    The ocamlgraph library is distributed under the LGPL (from +

    2.11 Copyright information

    The ocamlgraph library is distributed under the LGPL (from http://www.lri.fr/~filliatr/ftp/ocamlgraph/); we include a snapshot for convenience. For its authorship and copyright information see the files therein.

    All other files are distributed under the BSD-style licence in LICENCE.

    -

    3  A minimal Ott source file: the untyped CBV lambda calculus

    Fig. 1 shows an Ott source file for an untyped call-by-value +

    3 A minimal Ott source file: the untyped CBV lambda calculus

    Fig. ‍1 shows an Ott source file for an untyped call-by-value (CBV) lambda calculus. This section explains the basic features that appear there, while in the following sections we show what must be added to generate typeset output, proof assistant definitions, and other things. -


    -
    -
    
    +


    +
    +
    
     % minimal
    -metavar termvar, x ::=  
    +metavar termvar, x ::=  
     
    -grammar
    -t :: ’t_’ ::=              
    -  | x            ::  :: Var
    -  | \ x . t      ::  :: Lam
    -  | t t’         ::  :: App 
    -  | ( t )        :: S:: Paren
    -  | { t / x } t’ :: M:: Tsub  
    +grammar
    +t :: ’t_’ ::=              
    +  | x            ::  :: Var
    +  | \ x . t      ::  :: Lam
    +  | t t’         ::  :: App 
    +  | ( t )        :: S:: Paren
    +  | { t / x } t’ :: M:: Tsub  
     
    -v :: ’v_’ ::=              
    -  | \ x . t      ::  :: Lam 
    +v :: ’v_’ ::=              
    +  | \ x . t      ::  :: Lam 
     
    -subrules
    -  v <:: t
    +subrules
    +  v <:: t
     
    -defns
    -Jop ::::=
    +defns
    +Jop ::::=
     
    - defn
    - t1 --> t2 :: ::reduce::by
    + defn
    + t1 --> t2 :: ::reduce::by
     
     
    -    --------------------------  :: ax_app
    -    (\x.t12) v2 -->  {v2/x}t12
    +    --------------------------  :: ax_app
    +    (\x.t12) v2 -->  {v2/x}t12
     
         t1 --> t1’
    -    -------------- :: ctx_app_fun
    +    -------------- :: ctx_app_fun
         t1 t --> t1’ t
     
         t1 --> t1’
    -    -------------- :: ctx_app_arg
    +    -------------- :: ctx_app_arg
         v t1 --> v t1’
    -
    +
    -
    +
    Figure 1: Source: test10.0.ott
    -

    +


    The figure is colourised, with Ott keywords like this and Ott symbols such as | and ::. Other user-specific input appears like this. Any text between % and the next newline is @@ -534,27 +537,27 @@

    3  A minimal Ott source file: the untyp t, and one for values v. This specifies the concrete syntax of object-language terms, the abstract syntax representations for proof-assistant mathematics, -and the syntax of symbolic terms to be used in semantic rules.

    Each rule has a rule name prefix (e.g. ’t_’) and then a list +and the syntax of symbolic terms to be used in semantic rules.

    Each rule has a rule name prefix (e.g. ‍’t_’) and then a list of productions. Each production, e.g.

       | \ x . t      ::  :: Lam
     

    -specifies a syntactic form as a list of elements, here ‘\’, -‘x’, ‘.’, and ‘t’, each of which is either a -metavariable (the ‘x’), a nonterminal (the ‘t’), or a +specifies a syntactic form as a list of elements, here ‘\’, +‘x’, ‘.’, and ‘t’, each of which is either a +metavariable (the ‘x’), a nonterminal (the ‘t’), or a terminal (\ . ( ) { } / -->). Within productions all elements must be whitespace-separated, so that the tool can deduce which are terminals. In the symbolic terms in the semantic rules below, however, whitespace is required only where necessary. -A few terminals have to be quoted (with '') if they appear in a grammar, e.g. to +A few terminals have to be quoted (with '') if they appear in a grammar, e.g. to use | as an object-language token, as they are part of the Ott syntax, but they do not have to be quoted at usage points. (If one accidentally omits inter-token whitespace in the grammar, the output of Ott can be surprising. This is best diagnosed by looking at the colourised ASCII or LaTeX output from Ott.)

    Metavariables and nonterminals can be formed from the specified -metavariable and nonterminal roots by appending a suffix, e.g. the -nonterminal t' in the App and Tsub productions.

    Between the ::’s is an optional meta flag M or S. Non-meta +metavariable and nonterminal roots by appending a suffix, e.g. ‍the +nonterminal t' in the App and Tsub productions.

    Between the ::’s is an optional meta flag M or S. Non-meta productions give rise to clauses of datatype definitions in the Isabelle/Coq/HOL output, whereas meta productions do not. Later, we will see how the user can specify how meta syntax should be translated @@ -562,36 +565,36 @@

    3  A minimal Ott source file: the untyp and S are identical except that productions with the latter are admitted when parsing example concrete terms; the S tag is thus appropriate for lightweight syntactic sugar, such as productions for -parentheses. One can also use another flag, e.g. X, along with the command-line option -tex_suppress_category, to suppress -productions in the generated LaTeX.

    Each production has a production name (e.g. t_Lam), composed of -the rule name prefix (here t_) and the production name kernel -that follows the ::’s (here Lam). The production name is +parentheses. One can also use another flag, e.g. ‍X, along with the command-line option -tex_suppress_category, to suppress +productions in the generated LaTeX.

    Each production has a production name (e.g. ‍t_Lam), composed of +the rule name prefix (here ‍t_) and the production name kernel +that follows the ::’s (here ‍Lam). The production name is used as a constructor name in the generated Isabelle/Coq/HOL.

    The tool supports arbitrary context-free grammars, extended with -special constructs for list forms (c.f. §12).

    Following the grammar in this example is a subrule +special constructs for list forms (c.f. ‍§12).

    Following the grammar in this example is a subrule declaration

       subrules
         v <:: t
     

    -declaring that the v grammar rule (of values) is a -subgrammar of the t rule (of terms). The tool checks that -there is in fact a subgrammar relationship, i.e. that for each +declaring that the v grammar rule (of values) is a +subgrammar of the t rule (of terms). The tool checks that +there is in fact a subgrammar relationship, i.e. ‍that for each production of the lower rule there exists a production of the higher rule with corresponding elements (up to the subrule relation). The subrule declaration means that, in the semantic rules below, we -will be able to use v’s in places where the grammar specifies t’s. +will be able to use v’s in places where the grammar specifies t’s. In the generated Isabelle/Coq/HOL for this example only one free -datatype will be generated, for the t rule, while for the v -rule we generate an is_v predicate over the t type. Usages of -v nonterminals in the semantic rules will have instances of this +datatype will be generated, for the t rule, while for the v +rule we generate an is_v predicate over the t type. Usages of +v nonterminals in the semantic rules will have instances of this predicate automatically inserted.

    Finally, we give a collection of definitions of inductive relations. In this example there is just one family of definitions (of -operational judgements), introduced by the defns Jop; it contains just one -definition of a relation, called reduce. +operational judgements), introduced by the defns Jop; it contains just one +definition of a relation, called reduce. In general there may be many defns blocks, each of which introduces a mutually recursive collection of defns. The relation definition -defn ... +defn ... also includes a grammar production specifying how elements of the relation can be written and typeset, here

      t1 --> t2
    @@ -618,7 +621,7 @@ 

    3  A minimal Ott source file: the untyp Each rule has a name, composed of a definition family prefix (here empty), a definition prefix (here also empty) and a kernel -(the ctx_app_arg). +(the ‍ctx_app_arg).

    The symbolic terms in semantic rules are parsed with a scannerless parser, built using parser combinators over character-list inputs. The parser searches for all parses of the input. If none are found, the ASCII @@ -639,9 +642,9 @@

    3  A minimal Ott source file: the untyp typical in semantic rules. Their performance on large (whole-program size) examples is untested. To resolve ambiguity one can add metaproductions for parentheses (as -in Fig. 1), or +in Fig. ‍1), or production-name annotations in particular symbolic terms, -e.g. the :t_tsub: in the AppAbs rule of the POPLmark +e.g. ‍the :t_tsub: in the AppAbs rule of the POPLmark example, test7.ott. There is currently no support for precedence or associativity.

    This file is included in the distribution as @@ -662,83 +665,83 @@

    3  A minimal Ott source file: the untyp line. To suppress the echo of the definition, add -show_sort false and -show_defns false.

    -

    3.1  Index variables

    +

    3.1 Index variables

    In addition to the metavar declarations above, the user can declare any number of distinguished index metavariables, e.g. by:

       indexvar index, i, j, n, m ::= {{ isa num }} {{ coq nat }} {{ hol num }} 
     

    -Given such a declaration, index, i, j, n -and m can be used in suffixes, e.g. in the production +Given such a declaration, index, i, j, n +and m can be used in suffixes, e.g. ‍in the production

        |  ( t1 , .... , tn )           :: :: Tuple
     

    There is a fixed ad-hoc language of suffixes, including numbers, primes, and index variables (see §23). Index metavariables cannot themselves be suffixed.

    -

    4  Generating LaTeX

    The example from the previous section can already be used to generate +

    4 Generating LaTeX

    The example from the previous section can already be used to generate LaTeX, for example by executing

        bin/ott -i tests/test10.0.ott -o out.tex 
     

    to produce a LaTeX file out.tex. One often needs to fine-tune the default typesetting, as illustrated in -Figure 2 (the Ott source) and Figure 3 +Figure ‍2 (the Ott source) and Figure ‍3 (the resulting LaTeX). -(The latter was built using the additional option -tex_show_meta false, to +(The latter was built using the additional option -tex_show_meta false, to suppress display of the metaproductions.) -


    -
    -
    
    +


    +
    +
    
     % minimal + latex + comments
    -metavar termvar, x ::=   
    -  {{ tex \mathit{[[termvar]]} }}
    +metavar termvar, x ::=   
    +  {{ tex \mathit{[[termvar]]} }}
     
    -grammar
    -t :: ’t_’ ::=                               {{ com term    }}
    -  | x            ::  :: Var                   {{ com variable}}
    -  | \ x . t      ::  :: Lam                   {{ com lambda  }}
    -  | t t’         ::  :: App                   {{ com app     }}
    -  | ( t )        :: S:: Paren      
    -  | { t / x } t’ :: M:: Tsub  
    +grammar
    +t :: ’t_’ ::=                               {{ com term    }}
    +  | x            ::  :: Var                   {{ com variable}}
    +  | \ x . t      ::  :: Lam                   {{ com lambda  }}
    +  | t t’         ::  :: App                   {{ com app     }}
    +  | ( t )        :: S:: Paren      
    +  | { t / x } t’ :: M:: Tsub  
     
    -v :: ’v_’ ::=                               {{ com value   }}
    -  | \ x . t      ::  :: Lam                   {{ com lambda  }}
    +v :: ’v_’ ::=                               {{ com value   }}
    +  | \ x . t      ::  :: Lam                   {{ com lambda  }}
     
    -terminals :: ’terminals_’ ::=
    -  | \            ::  :: lambda  {{ tex \lambda }}
    -  | -->          ::  :: red     {{ tex \longrightarrow }}
    +terminals :: ’terminals_’ ::=
    +  | \            ::  :: lambda  {{ tex \lambda }}
    +  | -->          ::  :: red     {{ tex \longrightarrow }}
     
    -subrules
    -  v <:: t
    +subrules
    +  v <:: t
     
    -defns
    -Jop ::::=
    +defns
    +Jop ::::=
     
    - defn
    - t1 --> t2 :: ::reduce::{{ com $[[t1]]$ reduces to $[[t2]]$}} by
    + defn
    + t1 --> t2 :: ::reduce::{{ com $[[t1]]$ reduces to $[[t2]]$}} by
     
     
    -    --------------------------  :: ax_app
    -    (\x.t12) v2 -->  {v2/x}t12
    +    --------------------------  :: ax_app
    +    (\x.t12) v2 -->  {v2/x}t12
     
         t1 --> t1’
    -    -------------- :: ctx_app_fun
    +    -------------- :: ctx_app_fun
         t1 t --> t1’ t
     
         t1 --> t1’
    -    -------------- :: ctx_app_arg
    +    -------------- :: ctx_app_arg
         v t1 --> v t1’
    -
    +
    -
    +
    Figure 2: Source: test10.2.ott
    -


    +


    Figure 3: Generated LaTeX: test10.2.tex
    -

    +


    The source file has three additions to the previous file. Firstly, the metavar declaration is annotated with a specification of how metavariables should be translated to LaTeX: @@ -773,20 +776,20 @@

    4  Generating LaTeX

    attached to a production, and the {{ com [[t1]] reduces to [[t2]]}} attached to a semantic relation. These appear in the LaTeX output -as shown in Figure 3.

    +as shown in Figure ‍3.

    -

    4.1  Specifying LaTeX for productions

    +

    4.1 Specifying LaTeX for productions

    One can also specify tex translations for productions, overriding the default -LaTeX typesetting, e.g. as in this example of +LaTeX typesetting, e.g. ‍as in this example of a type abstraction production.

         |  X <: T . t   :: :: TLam   {{ tex \Lambda [[X]] [[<:]] [[T]]. \, [[t]] }}
     

    These homomorphisms, or homs1, can refer to the metavariables and -nonterminals that occur in the production, e.g. the [[X]], +nonterminals that occur in the production, e.g. ‍the [[X]], [[T]], and [[t]] in the tex hom above, interleaved with arbitrary strings and with typeset elements of the -terminals grammar, e.g. the [[<:]].

    Homomorphisms are applied recursively down the structure of symbolic +terminals grammar, e.g. ‍the [[<:]].

    Homomorphisms are applied recursively down the structure of symbolic terms. For example, an F<: term

      (\X<:T11.t12) [T2]
     

    @@ -798,10 +801,10 @@

    4.1  Specifying LaTeX for production

    -Note the X, T11 and t12 of the symbolic term are -used to instantiate the formal parameters X, T and -t of the homomorphism definition clause. -If the t itself had compound term structure, e.g. as below +Note the X, T11 and t12 of the symbolic term are +used to instantiate the formal parameters X, T and +t of the homomorphism definition clause. +If the t itself had compound term structure, e.g. as below

      (\X<:T. \X'<:T'.x)
     

    the homomorphism would be applied recursively, producing @@ -814,10 +817,10 @@

    4.1  Specifying LaTeX for production

    Where there is no user-supplied homomorphism clause the LaTeX pretty-printing defaults to a sequence of the individual items -separated by thin spaces (\,), -with reasonable default fonts and making use of the terminals grammar where appropriate.

    +separated by thin spaces (\,), +with reasonable default fonts and making use of the terminals grammar where appropriate.

    -

    4.2  Specifying LaTeX for grammar rules

    +

    4.2 Specifying LaTeX for grammar rules

    Grammar rules can include a tex hom specifying how all the nonterminal roots should be typeset, e.g.

    @@ -832,35 +835,35 @@ 

    4.2  Specifying LaTeX for grammar ru | empty :: :: empty | G , x : T :: :: term

    -permitting the user to write G', D12 etc. in symbolic +permitting the user to write G', D12 etc. ‍in symbolic terms, to be typeset as etc.

    -

    4.3  Using the LaTeX code

    The generated LaTeX code can be used in two main ways. +

    4.3 Using the LaTeX code

    The generated LaTeX code can be used in two main ways. By default, Ott generates a stand-alone LaTeX file, -with a standard wrapper (including a \documentclass, various +with a standard wrapper (including a \documentclass, various macro definitions, and a main body), that gives the complete system definition.

    The default header can be overridden by writing - embed {{ tex-wrap-pre ... }} and the default footer by -writing embed {{ tex-wrap-post ... }} . Alternatively, the -program option -tex_wrap false with the -tex_wrap false + embed {{ tex-wrap-pre ... }} and the default footer by +writing embed {{ tex-wrap-post ... }} . Alternatively, the +program option -tex_wrap false with the -tex_wrap false command-line argument, one can generate a file that can be included in other LaTeX files, that just defines macros to typeset various -parts of the system (-tex_wrap false overrides any -tex-wrap-pre/tex-wrap-post embeds).

    The generated LaTeX output is factored into individual LaTeX +parts of the system (-tex_wrap false overrides any +tex-wrap-pre/tex-wrap-post embeds).

    The generated LaTeX output is factored into individual LaTeX commands: for the metavariable declarations, each rule of the syntax -definition, the collected syntax (\ottgrammar), each rule of the inductive relation +definition, the collected syntax (\ottgrammar), each rule of the inductive relation definitions, the collected rules for each relation, the collected -rules for each defns block, the union of those -(\ottdefns) and the whole (\ottall). +rules for each defns block, the union of those +(\ottdefns) and the whole (\ottall). This makes it possible to quote individual parts of the definition, possibly out-of-order, in a paper or technical report.

    If one needs to include more than one system in a single LaTeX -document, the ott prefix can be replaced using the --tex_name_prefix command-line argument.

    The generated LaTeX is factored through some common style macros, -e.g. to typeset a comment, a production, and a grammar. If necessary -these can be redefined in an embed block (see Section 8.1). -For example, the file tests/squishtex.ott +document, the ott prefix can be replaced using the +-tex_name_prefix command-line argument.

    The generated LaTeX is factored through some common style macros, +e.g. ‍to typeset a comment, a production, and a grammar. If necessary +these can be redefined in an embed block (see Section ‍8.1). +For example, the file tests/squishtex.ott

     embed 
      {{ tex-preamble
    @@ -877,69 +880,69 @@ 

    4.3  Using the LaTeX code

    -

    5  Generating proof assistant definitions

    To generate proof assistant definitions, for Coq, Isabelle, and HOL, -the minimal Ott source file of Section 3/Figure 1 must -be extended with a modest amount of additional data, as shown in Figure 4. +

    5 Generating proof assistant definitions

    To generate proof assistant definitions, for Coq, Isabelle, and HOL, +the minimal Ott source file of Section ‍3/Figure ‍1 must +be extended with a modest amount of additional data, as shown in Figure ‍4. Executing

        bin/ott  -i tests/test10.4.ott  -o out.v  -o out.thy  -o outScript.sml 
     

    generates Coq out.v, Isabelle out.thy, and HOL -outScript.sml, shown in Figures 5, 6, and 7. +outScript.sml, shown in Figures ‍5, 6, and 7. The additional data can be combined with the annotations for LaTeX of the previous section, but those are omitted here. -


    -
    -
    
    +


    +
    +
    
     % minimal                    + binding + subst + coq/hol/isa
    -metavar termvar, x ::=  
    -{{ isa string}} {{ coq nat}} {{ hol string}} {{ coq-equality }}
    +metavar termvar, x ::=  
    +{{ isa string}} {{ coq nat}} {{ hol string}} {{ coq-equality }}
     
    -grammar
    -t :: ’t_’ ::=                               
    -  | x            ::  :: Var                  
    -  | \ x . t      ::  :: Lam     (+ bind x in t +)
    -  | t t’         ::  :: App                  
    -  | ( t )        :: S:: Paren   {{ icho [[t]]  }} 
    -  | { t / x } t’ :: M:: Tsub    {{ icho (tsubst_t [[t]] [[x]] [[t’]])}}
    +grammar
    +t :: ’t_’ ::=                               
    +  | x            ::  :: Var                  
    +  | \ x . t      ::  :: Lam     (+ bind x in t +)
    +  | t t’         ::  :: App                  
    +  | ( t )        :: S:: Paren   {{ icho [[t]]  }} 
    +  | { t / x } t’ :: M:: Tsub    {{ icho (tsubst_t [[t]] [[x]] [[t’]])}}
     
    -v :: ’v_’ ::=                              
    -  | \ x . t      ::  :: Lam                
    +v :: ’v_’ ::=                              
    +  | \ x . t      ::  :: Lam                
     
    -subrules
    -  v <:: t
    +subrules
    +  v <:: t
     
    -substitutions
    -  single t x :: tsubst 
    +substitutions
    +  single t x :: tsubst 
     
    -defns
    -Jop ::::=
    +defns
    +Jop ::::=
     
    - defn
    - t1 --> t2 :: ::reduce::by
    + defn
    + t1 --> t2 :: ::reduce::by
     
     
    -    --------------------------  :: ax_app
    -    (\x.t12) v2 -->  {v2/x}t12
    +    --------------------------  :: ax_app
    +    (\x.t12) v2 -->  {v2/x}t12
     
         t1 --> t1’
    -    -------------- :: ctx_app_fun
    +    -------------- :: ctx_app_fun
         t1 t --> t1’ t
     
         t1 --> t1’
    -    -------------- :: ctx_app_arg
    +    -------------- :: ctx_app_arg
         v t1 --> v t1’
    -
    +
    -
    +
    Figure 4: Source: test10.4.ott
    -

    +


    We add four things. First, we specify proof assistant types to represent object-language variables — in this example, choosing the string type of @@ -948,7 +951,7 @@

    5  Generating proof assistant definitio metavar termvar, x ::= {{ isa string}} {{ coq nat}} {{ hol string}} {{ coq-equality }}

    -For Coq output, one can specify {{ coq-equality proof-script }} +For Coq output, one can specify {{ coq-equality proof-script }} to build a decidable equality over the Coq representation type using the proof proof-script. If the script is omitted, as in this example, it defaults @@ -957,10 +960,10 @@

    5  Generating proof assistant definitio decide equality; auto with ott_coq_equality arith. Defined.

    -where the ott_coq_equality database contains the decidable +where the ott_coq_equality database contains the decidable equalities of the representation types defined in the source. It is possible to suppress type generation for specific metavariables or nonterminals, by adding the -declaration {{ phantom }}. This is useful in some cases, for +declaration {{ phantom }}. This is useful in some cases, for instance to avoid duplicate definitions of types already defined in an imported library. Any type homs are taken into account when the metavariable or nonterminal root is output as a type.

    Second, we specify what the binding is in the object language, with @@ -969,7 +972,7 @@

    5  Generating proof assistant definitio

       | \ x . t      ::  :: Lam     (+ bind x in t +)
     

    -Section 9 describes the full language of binding specifications.

    Third, we add a block +Section ‍9 describes the full language of binding specifications.

    Third, we add a block

       substitutions
         single t x :: tsubst 
    @@ -980,8 +983,8 @@ 

    5  Generating proof assistant definitio substitutand/substitutee pairs) can also be generated with the keyword multiple. Substitution functions are generated for all rules of the grammar for -which they might be required — here, just over t, with a -function named tsubst_t.

    Finally, we specify translations for the metaproductions: +which they might be required — here, just over t, with a +function named tsubst_t.

    Finally, we specify translations for the metaproductions:

       | ( t )        :: S:: Paren   {{ icho [[t]]  }} 
       | { t / x } t’ :: M:: Tsub    {{ icho (tsubst_t [[t]] [[x]] [[t’]])}}
    @@ -1002,9 +1005,9 @@ 

    5  Generating proof assistant definitio There are also abbreviated forms ich, ic, ch, and ih. The body of a proof assistant hom should normally include outer parentheses, as in the Tsub hom above, so that it is -parsed correctly by the proof assistant in all contexts.


    -
    -
    (* generated by Ott 0.32 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
    +parsed correctly by the proof assistant in all contexts.


    +
    +
    (* generated by Ott 0.33 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
     
     Require Import Arith.
     Require Import Bool.
    @@ -1065,13 +1068,13 @@ 

    5  Generating proof assistant definitio reduce (t_app v t1) (t_app v t1'). -

    -
    +
    +
    Figure 5: Generated Coq:test10.v
    -


    -
    -
    (* generated by Ott 0.32 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
    +


    +
    +
    (* generated by Ott 0.33 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
     theory test10
     imports Main
     begin
    @@ -1120,13 +1123,13 @@ 

    5  Generating proof assistant definitio -

    -
    +

    +
    Figure 6: Generated Isabelle:test10.thy
    -


    -
    -
    (* generated by Ott 0.32 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
    +


    +
    +
    (* generated by Ott 0.33 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
     (* to compile: Holmake test10Theory.uo   *)
     (* for interactive use:
       app load ["pred_setTheory","finite_mapTheory","stringTheory","containerTheory","ottLib"];
    @@ -1189,13 +1192,13 @@ 

    5  Generating proof assistant definitio -

    -
    +

    +
    Figure 7: Generated HOL:test10Script.sml
    -

    +

    -

    5.1  Proof assistant code for grammar rules

    +

    5.1 Proof assistant code for grammar rules

    The normal behaviour is to generate a free proof assistant type for each (non-subrule, non-phantom) grammar rule. For example, the Coq compilation for t here generates a free type with three @@ -1219,7 +1222,7 @@

    5.1  Proof assistant code for gramma the order in which they appear in the production. That can be overridden with an order hom. For example, if for some reason (perhaps compatibility with other Coq code) one wished the -arguments to t_Lam to be reversed: +arguments to t_Lam to be reversed:

       | t_Lam : t -> termvar -> t
     

    one could add an order hom as below. @@ -1235,9 +1238,9 @@

    5.1  Proof assistant code for gramma

    Here the {{ isa (termvar*t) list }} hom specifies that in Isabelle output this type be represented as an Isabelle -(termvar*t) list instead of the default free inductive type; +(termvar*t) list instead of the default free inductive type; all the productions are metaproductions (tagged M); and isa homs for each production specify how they should be translated into that Isabelle type. -This feature must be used with care, as any Ott-generated functions, e.g. substitution functions, cannot recurse through such user-defined types.

    Grammar rules (whether free or non-free) can also include a coq equality hom, instructing +This feature must be used with care, as any Ott-generated functions, e.g. ‍substitution functions, cannot recurse through such user-defined types.

    Grammar rules (whether free or non-free) can also include a coq equality hom, instructing the Coq code generator to derive a decidable equality for the Coq representation type. For example, the ML polymorphism Ott source of test8.ott includes the following. @@ -1249,7 +1252,7 @@

    5.1  Proof assistant code for gramma taken to be just its primary nonterminal root. Occasionally it is useful to work around a clash between a metavar or nonterminal primary root and a proof assistant symbol, -e.g. T in HOL or value in Isabelle. +e.g. ‍T in HOL or value in Isabelle. For this, one can add a coq, hol, isa, or ocaml hom to the primary nonterminal root. In the example below, the user can write T, T’ etc. in their Ott @@ -1262,15 +1265,15 @@

    5.1  Proof assistant code for gramma topologically sorted, to simplify the resulting induction principles.

    -

    5.2  Proof assistant code for inductive definitions

    +

    5.2 Proof assistant code for inductive definitions

    The semantic relations are defined with the proof-assistant inductive relations packages, -Inductive, Hol_reln, and inductive_set or inductive, respectively. +Inductive, Hol_reln, and inductive_set or inductive, respectively. Each defns block gives rise to a potentially mutually recursive definition of each defn inside it (it seems clearer not to do a topological sort here). Definition rules are expressed internally with symbolic terms. -We give a simplified grammar thereof in Fig. 17, omitting the +We give a simplified grammar thereof in Fig. ‍17, omitting the symbolic terms for list forms. A symbolic term st for a nonterminal root is either an explicit nonterminal or a node, the latter labelled with a production name and containing a list of @@ -1286,21 +1289,21 @@

    5.2  Proof assistant code for induct Nodes of non-meta productions are output as applications of the appropriate proof-assistant constructor (and, for a subrule, promoted to the corresponding constructor of a maximal rule).
  • Nodes of meta productions are transformed with the user-specified homomorphism.
  • Nodes of judgement forms are represented as applications of the defined relation in Coq and HOL, and as set-membership assertions in Isabelle. -
  • Lists of formulae (the formula_dots production, c.f.§12) are +
  • Lists of formulae (the formula_dots production, c.f.§12) are special-cased to proof-assistant conjunctions.
  • Further, for each nonterminal of a non-free grammar rule, -e.g. a usage of v' where v<::t, an additional premise -invoking the generated subrule predicate for the non-free rule is added, e.g. is_v v'. +e.g. ‍a usage of v' where v<::t, an additional premise +invoking the generated subrule predicate for the non-free rule is added, e.g. ‍is_v v'. For Coq and HOL, explicit quantifiers are introduced for all variables mentioned in the rule. -For HOL, rules are tagged with their rule name (using clause_name).

    +For HOL, rules are tagged with their rule name (using clause_name).

    -

    5.3  Representation of binding

    +

    5.3 Representation of binding

    At present the generated Isabelle/Coq/HOL uses fully concrete representations of variables in terms, without any notion of alpha -equivalence, as one can see in Fig. 6: -see the t datatype of terms and the tsubst_t +equivalence, as one can see in Fig. ‍6: +see the t datatype of terms and the tsubst_t substitution function there. An experimental Coq backend generates definitions in locally-nameless style for a subset of the Ott metalanguage. This is work-in-progress, and it is extensively documented in http://moscova.inria.fr/~zappa/projects/ln_ott/. We intend in future to generate other representations, and in some @@ -1323,31 +1326,31 @@

    5.3  Representation of binding

    <

    available from the Ott web page.

    -

    5.4  Helper functions for free variable and substitution functions

    The generated free variable and substitution functions in the Coq output -(e.g., in Figure 5) often rely on a few standard library functions: -list_mem, list_assoc, list_minus, list_minus2. +

    5.4 Helper functions for free variable and substitution functions

    The generated free variable and substitution functions in the Coq output +(e.g., in Figure ‍5) often rely on a few standard library functions: +list_mem, list_assoc, list_minus, list_minus2. In order to avoid dependencies on external libraries for defining those functions, by default Ott generates the definitions for any such functions it uses. It is possible to turn off the generation of definitions for these such functions by writing the following directive early on in the source file:

     embed {{ coq-lib list_mem list_minus }}
    -

    This instructs Ott to avoid generating definition for list_mem and -list_minus, but to continue generating definitions for other functions -such as list_assoc and list_minus2.

    Note about list_minus2: -Instead of using the function list_minus2, earlier versions of Ott -generated equivalent code based on list_filter, which was more +

    This instructs Ott to avoid generating definition for list_mem and +list_minus, but to continue generating definitions for other functions +such as list_assoc and list_minus2.

    Note about list_minus2: +Instead of using the function list_minus2, earlier versions of Ott +generated equivalent code based on list_filter, which was more difficult to reason about. For backwards compatibility, however, we provide the -command-line option -coq_use_filter_fn for generating a definition using +command-line option -coq_use_filter_fn for generating a definition using the older code pattern.

    -

    5.5  Correctness of the generated proof assistant code

    +

    5.5 Correctness of the generated proof assistant code

    We have attempted to ensure that the proof assistant definitions generated by Ott are well-formed and what the user would intend. This is not guaranteed, however, for several reasons: (1) There may be name clashes between Ott-generated identifiers and proof assistant built-in identifiers (or, in pathological cases, even among different Ott-generated identifiers). (2) In some cases we depend on automatic -proof procedures, e.g. for HOL definitions. These work in our test +proof procedures, e.g. ‍for HOL definitions. These work in our test cases, but it is hard to ensure that they will in all cases. More importantly, (3) the generation process is complex, so it is quite possible that there is either a bug in Ott or a mismatch between the @@ -1357,7 +1360,7 @@

    5.5  Correctness of the generated pr times over, in the process of proving metatheoretic results, so we do not consider it a major issue.

    -

    5.6  Using the generated proof assistant code

    +

    5.6 Using the generated proof assistant code

    Note added 2017-11-30: the following is out of date.

    Ott builds code for

    Coq 8.3http://coq.inria.fr/
    HOL 4 (the current svn version)http://hol.sourceforge.net/
    Isabelle/HOL (Isabelle 2011)http://isabelle.in.tum.de/ @@ -1367,22 +1370,22 @@

    5.6  Using the generated proof assis (Coq out.v, Isabelle out.thy, and HOL outScript.sml), the various proof assistants can be invoked as follows.

    -

    5.6.1  Coq

    +

    5.6.1 Coq

    First run

      make
     

    in the coq directory of the distribution, to build the auxiliary files. -These include a core file (ott_list_core) of definitions that +These include a core file (ott_list_core) of definitions that are used in Ott-generated output. At present these are only required when Coq native lists are used. There are also various lemmas (in -ott_list.v) which may be useful; they can be made available with -Require Import ott_list.

    For batch mode run +ott_list.v) which may be useful; they can be made available with +Require Import ott_list.

    For batch mode run

      coqc -I coq  out.v
     

    -where coq is the path to the coq directory of the distribution.

    The experimental locally-nameless backend requires the Metatheory library by Arthur Chargueraud, available from the project web page.

    +where coq is the path to the coq directory of the distribution.

    The experimental locally-nameless backend requires the Metatheory library by Arthur Chargueraud, available from the project web page.

    -

    5.6.2  HOL

    +

    5.6.2 HOL

    First run

      Holmake
     

    @@ -1390,17 +1393,17 @@

    5.6.2  HOL

    batch mode run

      Holmake -I hol  outTheory.uo
     

    -where hol is the path to the hol directory of the distribution. +where hol is the path to the hol directory of the distribution. For interactive mode, run

      hol -I hol
     

    -inside an editor window (where the second hol is again the path -to the hol directory of the distribution), and in -another window view the outScript.sml file. First -paste in the app load command from a comment at the top of the +inside an editor window (where the second hol is again the path +to the hol directory of the distribution), and in +another window view the outScript.sml file. First +paste in the app load command from a comment at the top of the file, then paste in the remainder.

    -

    5.6.3  Isabelle

    +

    5.6.3 Isabelle

    For batch mode:

      echo 'ML_command {* (use_thy "Tmp"; OS.Process.exit OS.Process.success) handle e => (OS.Process.exit OS.Process.failure); *}' | /usr/local/Isabelle/bin/isabelle tty 
     

    @@ -1408,7 +1411,7 @@

    5.6.3  Isabelle

    -

    6  Judgments and formulae

    In a semantic rule, for example +

    6 Judgments and formulae

    In a semantic rule, for example

         t1 --> t1’
         -------------- :: ctx_app_arg
    @@ -1449,7 +1452,7 @@ 

    6  Judgments and formulae

    -

    6.1  Naming of premises for the Coq backend

    +

    6.1 Naming of premises for the Coq backend

    It is possible to specify the names of premises of inductive predicates; these names are then used by the Coq backend, and are often useful in proofs. For instance, we can call RED the hypothesis in the rule below @@ -1470,7 +1473,7 @@

    6.1  Naming of premises for the Coq the rightmost place on the hypothesis line, and must be enclosed (without spaces) between the [[: and ]] parentheses.

    -

    6.2  In-line embedded prover code in premises

    +

    6.2 In-line embedded prover code in premises

    Instead of adding a formula production, one can directly embed prover code as a premise, delimited as below by {{ and }}. Within that, text will be echoed @@ -1483,102 +1486,102 @@

    6.2  In-line embedded prover code in e1=e2 . k |env --tau--> lval(e1) . [__=c e2] . k |env

    -

    6.3  User syntax

    +

    6.3 User syntax

    The tool also synthesises a user_syntax grammar of all the user syntax, for example:

    This is used for parsing top-level strings, for example when filtering embedded code (§8).

    -

    7  Concrete terms and OCaml generation

    In semantic definitions, one typically never uses concrete variables, +

    7 Concrete terms and OCaml generation

    In semantic definitions, one typically never uses concrete variables, only metavariables that range over them. In examples, however, one may need either a mix of concrete variables and metavariables, or, for strictly concrete terms, to restrict to -just the former (and also to prohibit symbolic nonterminals).

    Figure 2 combines the LaTeX and proof assistant +just the former (and also to prohibit symbolic nonterminals).

    Figure ‍2 combines the LaTeX and proof assistant annotations of Sections 3 and 4, adding a {{ lex alphanum}} hom to the metavar declaration to specify the lexical form of concrete variables of this sort. At present a lex homomorphism must have body either -Alphanum (standing for [A-Z]([A-Z]|[a-z]|[0-9]|'|_)*), -alphanum (for ([A-Z]|[a-z])([A-Z]|[a-z]|[0-9]|'|_)*), -alphanum0 (for [a-z]([A-Z]|[a-z]|[0-9]|'|_)*), or -numeral (for [0-9][0-9]*); more general regular expressions are not supported. +Alphanum (standing for [A-Z]([A-Z]|[a-z]|[0-9]|'|_)*), +alphanum (for ([A-Z]|[a-z])([A-Z]|[a-z]|[0-9]|'|_)*), +alphanum0 (for [a-z]([A-Z]|[a-z]|[0-9]|'|_)*), or +numeral (for [0-9][0-9]*); more general regular expressions are not supported. An identifier that can be ambiguously lexed as either a concrete or -symbolic metavariable, e.g. x in the scope of the above +symbolic metavariable, e.g. ‍x in the scope of the above declaration, will be taken to be symbolic. To restrict the parser to strictly concrete terms only, one can add a -:concrete: prefix, as shown in Figure 10.

    One can also specify how concrete variables should be LaTeX’d or -translated into a proof assistant, e.g. with +:concrete: prefix, as shown in Figure ‍10.

    One can also specify how concrete variables should be LaTeX’d or +translated into a proof assistant, e.g. ‍with homomorphisms {{ texvar \mathrm{[[termvar]]}} and {{ isavar ’’[[termvar]]’’}} -(and similarly coqvar, holvar, and ocamlvar).

    Figure 2 also specifies an OCaml representation type for +(and similarly coqvar, holvar, and ocamlvar).

    Figure ‍2 also specifies an OCaml representation type for variables, with the metavar hom {{ ocaml int}}. Executing

        bin/ott -i tests/test10.ott -o test10.ml  
     

    -produces the OCaml code shown in Figure 9, including OCaml +produces the OCaml code shown in Figure ‍9, including OCaml types to represent the abstract syntax, and auxiliary functions for subrules and substitutions. This does not implement the semantic rules. In some cases the various proof assistant code extraction -facilities can be used — see Section 21.


    -
    -
    
    +facilities can be used — see Section ‍21.


    +
    +
    
     % all
    -metavar termvar, x ::=   {{ com  term variable }} 
    -{{ isa string}} {{ coq nat}} {{ hol string}} {{ coq-equality }}
    -{{ ocaml int}} {{ lex alphanum}} {{ tex \mathit{[[termvar]]} }}
    +metavar termvar, x ::=   {{ com  term variable }} 
    +{{ isa string}} {{ coq nat}} {{ hol string}} {{ coq-equality }}
    +{{ ocaml int}} {{ lex alphanum}} {{ tex \mathit{[[termvar]]} }}
     
    -grammar
    -t :: ’t_’ ::=                               {{ com term    }}
    -  | x            ::  :: Var                   {{ com variable}}
    -  | \ x . t      ::  :: Lam (+ bind x in t +) {{ com lambda  }}
    -  | t t’         ::  :: App                   {{ com app     }}
    -  | ( t )        :: S:: Paren                 {{ icho [[t]]  }} 
    -  | { t / x } t’ :: M:: Tsub  
    -                        {{ icho (tsubst_t [[t]] [[x]] [[t’]])}}
    +grammar
    +t :: ’t_’ ::=                               {{ com term    }}
    +  | x            ::  :: Var                   {{ com variable}}
    +  | \ x . t      ::  :: Lam (+ bind x in t +) {{ com lambda  }}
    +  | t t’         ::  :: App                   {{ com app     }}
    +  | ( t )        :: S:: Paren                 {{ icho [[t]]  }} 
    +  | { t / x } t’ :: M:: Tsub  
    +                        {{ icho (tsubst_t [[t]] [[x]] [[t’]])}}
     
    -v :: ’v_’ ::=                               {{ com value   }}
    -  | \ x . t      ::  :: Lam                   {{ com lambda  }}
    +v :: ’v_’ ::=                               {{ com value   }}
    +  | \ x . t      ::  :: Lam                   {{ com lambda  }}
     
    -terminals :: ’terminals_’ ::=
    -  | \            ::  :: lambda  {{ tex \lambda }}
    -  | -->          ::  :: red     {{ tex \longrightarrow }}
    +terminals :: ’terminals_’ ::=
    +  | \            ::  :: lambda  {{ tex \lambda }}
    +  | -->          ::  :: red     {{ tex \longrightarrow }}
     
    -subrules
    -  v <:: t
    +subrules
    +  v <:: t
     
    -substitutions
    -  single t x :: tsubst 
    +substitutions
    +  single t x :: tsubst 
     
    -defns
    -Jop ::::=
    +defns
    +Jop ::::=
     
    - defn
    - t1 --> t2 :: ::reduce::{{ com $[[t1]]$ reduces to $[[t2]]$}} by
    + defn
    + t1 --> t2 :: ::reduce::{{ com $[[t1]]$ reduces to $[[t2]]$}} by
     
     
    -    --------------------------  :: ax_app
    -    (\x.t12) v2 -->  {v2/x}t12
    +    --------------------------  :: ax_app
    +    (\x.t12) v2 -->  {v2/x}t12
     
         t1 --> t1’
    -    -------------- :: ctx_app_fun
    +    -------------- :: ctx_app_fun
         t1 t --> t1’ t
     
         t1 --> t1’
    -    -------------- :: ctx_app_arg
    +    -------------- :: ctx_app_arg
         v t1 --> v t1’
    -
    +
    -
    +
    Figure 8: Source: test10.7.ott
    -


    -
    -
    (* generated by Ott 0.32 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
    +


    +
    +
    (* generated by Ott 0.33 from: ../tests/test10.ott ../tests/non_super_tabular.ott *)
     
     type var = int (* term variable *)
     
    @@ -1609,17 +1612,17 @@ 

    7  Concrete terms and OCaml generation< (** definitions *) -

    -
    +
    +
    Figure 9: Generated OCaml code: test10.ml
    -

    +

    -

    8  Filtering: Using Ott syntax within LaTeX, Coq, Isabelle, +

    8 Filtering: Using Ott syntax within LaTeX, Coq, Isabelle, HOL, or OCaml

    -

    8.1  Filtering embedded code

    It is possible to embed arbitrary code in +

    8.1 Filtering embedded code

    It is possible to embed arbitrary code in the Ott source using an embed block, which can contain tex, coq, @@ -1627,10 +1630,10 @@

    8.1  Filtering embedded code

    isa, or ocaml homomorphisms, the bodies of which will appear in the respective output. -The embed keyword should be on a line by itself). For +The embed keyword should be on a line by itself). For example, -test8.ott contains the following to -define Coq and HOL remove_duplicates functions. +test8.ott contains the following to +define Coq and HOL remove_duplicates functions.

     embed
     {{ coq
    @@ -1650,10 +1653,10 @@ 

    8.1  Filtering embedded code

    Within the body of an embed homomorphism, any text between [[ and ]] will be parsed as a symbolic term (of the -user_syntax grammar) and pretty printed, so one can use user +user_syntax grammar) and pretty printed, so one can use user syntax within LaTeX or proof assistant code. An Isabelle example is below, defining an Isabelle function to calculate the order of a -type with productions unit, t*t', and t->t'. +type with productions unit, t*t', and t->t'.

     {{ isa
     consts
    @@ -1675,16 +1678,16 @@ 

    8.1  Filtering embedded code

    tex-preamble section.

    -

    8.2  Filtering files

    +

    8.2 Filtering files

    Similar processing can be carried out on separate files, using the -command-line options tex_filter, isa_filter, etc. +command-line options tex_filter, isa_filter, etc. Each of these takes two arguments, a source filename and a destination filename. In processing the source file, any text between [[ and ]] will be parsed as a symbolic term (of the -user_syntax grammar) and pretty printed in the appropriate +user_syntax grammar) and pretty printed in the appropriate style. All other text is simply echoed. -

    Typical usage for LaTeX would be something like this (from the Makefile +

    Typical usage for LaTeX would be something like this (from the Makefile used to produce this document):

    test7.tex: ../src/ott ../tests/test7.ott ../tests/test7tt.mng
              cd ../src; make tmp_test7_clean.ott
    @@ -1696,22 +1699,22 @@ 

    8.2  Filtering files

    -

    9  Binding specifications

    Our first example involved a production with a single binder: +

    9 Binding specifications

    Our first example involved a production with a single binder: -

    specified by the source shown in Figure 4: +

    specified by the source shown in Figure ‍4:

       | \ x . t      ::  :: Lam     (+ bind x in t +)
     

    in which a single variable binds in a single subterm. Realistic programming languages often have much more complex binding -structures, e.g. structured patterns, multiple mutually recursive let definitions, +structures, e.g. ‍structured patterns, multiple mutually recursive let definitions, comprehensions, or-patterns, and dependent record patterns.

    Ott has a flexible metalanguage for specifying binding structures, expressive enough to cover these. It comprises two forms of annotation on productions. @@ -1780,7 +1783,7 @@

    9  Binding specifications

    -

    10  Generating substitution and free variable functions

    The tool can generate Isabelle/Coq/HOL/OCaml code for both single and multiple +

    10 Generating substitution and free variable functions

    The tool can generate Isabelle/Coq/HOL/OCaml code for both single and multiple substitution functions. For example, the ML polymorphism Ott source of test8.ott includes the following.

    @@ -1840,19 +1843,19 @@ 

    10  Generating substitution and free va multiple typexpr typvar :: tsubst

    This causes the generation of two families of substitution -functions, one replacing a single value_name by a expr, -the other replacing multiple typvars by typexprs.

    Each family contains a function for each datatype for which it is +functions, one replacing a single value_name by a expr, +the other replacing multiple typvars by typexprs.

    Each family contains a function for each datatype for which it is required, so in that example there are functions -subst_expr for the first and tsubst_typexpr, -tsubst_typscheme and tsubst_G for the second.

    The functions for substitutions declared by +subst_expr for the first and tsubst_typexpr, +tsubst_typscheme and tsubst_G for the second.

    The functions for substitutions declared by

       substitutions
         single   this that :: name1 
         multiple this that :: name2 
     

    -replaces terms of productions consisting just of a single that by a -this. -Here this must be a nonterminal root, while that can be +replaces terms of productions consisting just of a single that by a +this. +Here this must be a nonterminal root, while that can be either a metavariable root or a nonterminal root (the latter possibility allows substitution for compound identifiers, though it is not clear that this is generally useful enough to be included). @@ -1880,7 +1883,7 @@

    10  Generating substitution and free va ftv_G :: "G => typvar list"

    -

    11  Locally-nameless representation

    The Coq backend of Ott includes experimental support for a +

    11 Locally-nameless representation

    The Coq backend of Ott includes experimental support for a locally-nameless representation (and co-finite quantification).

    The user must specify which metavariables require a locally-nameless representation via the repr-locally-nameless hom, e.g.:

    @@ -1980,29 +1983,29 @@ 

    11  Locally-nameless representation

    Current limitations: support for single binders only, no auxfn, Coq only.

    Disclaimer: to compile rule definitions, Ott applies blindly the algorithm described above. Although in most of the cases, this generates a correct and idiomatic representation of the language, some language constructs might not be faithfully translated. Please, let us know if you find one of these cases.

    If Ott is invoked with the -coq_lngen option, then the generated locally-nameless Coq code is compatible with Aydemir’s -lngen tool (http://www.cis.upenn.edu/ sweirich/papers/lngen/).

    +lngen tool (http://www.cis.upenn.edu/ ‍sweirich/papers/lngen/).

    -

    12  List forms

    Ott has direct support for lists, both as dot forms such as +

    12 List forms

    Ott has direct support for lists, both as dot forms such as and as list comprehensions such as . -Figure 13 shows an example semantic rule taken from our OCaml +Figure ‍13 shows an example semantic rule taken from our OCaml fragment semantics, as both the generated LaTeX and its Ott source, that involves several dot forms. -Other types commonly used in semantics, e.g. finite maps or sets, can +Other types commonly used in semantics, e.g. ‍finite maps or sets, can often be described with this list syntax in conjunction with type and metaproduction homs to specify the proof assistant representation. When using list forms, one usually also wants to add a list-of-formula production to the formula grammar, e.g. (as in -test17.10.ott): +test17.10.ott):

       formula :: formula_ ::=  
        |  judgement                       ::   :: judgement
        |  formula1 .. formulan            ::   :: dots
     

    The proof assistant code generation for such a production (which must -be named formula_dots) is special-cased to a list conjunction.


    +be named formula_dots) is special-cased to a list conjunction.


    
      E |- e1 : t1 ... E |- en : tn
      E |- field_name1 : t->t1 ... E |- field_namen : t->tn
    @@ -2014,9 +2017,9 @@ 

    12  List forms

    {field_name1=e1; ...; field_namen=en} : t

    -
    Figure 13: A sample OCaml semantic rule, in LaTeX and Ott source forms

    +

    -

    12.1  List dot forms

    Example productions for +

    12.1 List dot forms

    Example productions for record types, record terms, and record patterns are shown below, in both Ott source and LaTeX, taken from our F<: example. @@ -2038,7 +2041,7 @@

    12.1  List dot forms

    -

    12.2  List comprehension forms

    Lists can also be expressed as explicit list comprehensions, +

    12.2 List comprehension forms

    Lists can also be expressed as explicit list comprehensions, for more concise typesetting. Three different styles are supported, with no bounds, an upper bound, or a lower and upper bound. For example, in a symbolic @@ -2090,7 +2093,7 @@

    12.2  List comprehension forms

    < Similar comprehensions can be used in productions, for example lines 2–4 below. In addition, comprehensions in productions can specify a terminal to be used as a separator in concrete lists, as in lines 5–7 below. -(These examples are taken from test17.10.ott.) +(These examples are taken from test17.10.ott.)

    
       |  l1 = t1 ,  .. , ln = tn            :: :: Rec              {{ com dots }}
       |  </ li = ti // i           />       :: :: Rec_comp_none    {{ com comp }}
    @@ -2144,30 +2147,30 @@ 

    12.2  List comprehension forms

    < with the same bounds. Moreover, in a production, a list form in a bindspec or homomorphism must be in the same style and with the same bounds as the corresponding list form in the elements of the production.

    The comprehension form without an upper bound, -e.g. </ G |- ti:Ti // i />, +e.g. ‍</ G |- ti:Ti // i />, typeset as is not standard notation, but is often very useful. Many semantic rules involve lists -of matched length, e.g. of the +of matched length, e.g. ‍of the and here, but do not need to introduce an identifier for that length; omitting it keeps them concise.

    The default visual style for typesetting list comprehensions can be -overridden by redefining the LaTeX commands \ottcomp, -\ottcompu, and \ottcomplu in an embed section, as -in Section 4.3.

    In some cases one could make the typeset notation even less noisy, by +overridden by redefining the LaTeX commands \ottcomp, +\ottcompu, and \ottcomplu in an embed section, as +in Section ‍4.3.

    In some cases one could make the typeset notation even less noisy, by either omitting the superscript i or omitting both the superscript i and the subscript i’s on t and T. The first is unambiguous if there is at most one index on each element in the comprehension; the second if all the elements are indexed by the same thing (not the case for this example, but common for comprehensions of single elements, -e.g. << Ti // i>> for T). It is arguable that that +e.g. << Ti // i>> for T). It is arguable that that should be automated in future Ott releases, though it would bring the typeset and ASCII versions out of step.

    List comprehension forms can also be used in bindspecs and in homomorphisms.

    -

    12.3  Proof assistant code for list forms

    +

    12.3 Proof assistant code for list forms

    -

    12.3.1  Types

    +

    12.3.1 Types

    We have to choose proof assistant representations for productions involving list forms. For example, for a language with records one might write

    @@ -2180,13 +2183,13 @@ 

    12.3.1  Types

    In HOL and Isabelle we represent these simply with contructors whose argument types involve proof-assistant native list types, e.g. -the HOL list of pairs of a label and a t: +the HOL list of pairs of a label and a t:

      val _ = Hol_datatype ` 
       t = E_record of (label#t) list  `;
     

    For Coq we provide two alternatives: one can either use native lists, or lists can be translated away, depending on taste. -The choice is determined by the -coq_expand_list_types +The choice is determined by the -coq_expand_list_types command-line option. In the former case we generate an appropriate induction principle using nested fixpoints, as @@ -2205,11 +2208,11 @@

    12.3.1  Types

    with t : Set := E_record : list_label_t -> t .

    -These are included in the grammar topological sort, and utility functions, e.g. to +These are included in the grammar topological sort, and utility functions, e.g. ‍to make and unmake lists, are synthesised.

    -

    12.3.2  Terms (in inductive definition rules)

    +

    12.3.2 Terms (in inductive definition rules)

    Supporting list forms in the rules of an inductive definition requires some additional analysis. For example, consider the record typing rule below.

    We analyse the symbolic terms in the premises and conclusion to @@ -2220,7 +2223,7 @@

    12.3.2  Terms (in inductive defin variable for each such collection, with appropriate projections and list maps/foralls at the usage points. For example, the HOL for the above is essentially as follows, with -an l_t_Typ_list : (label#t#Typ) list. +an l_t_Typ_list : (label#t#Typ) list.

    (* Ty_Rcd *)  !(l_t_Typ_list:(label#t#Typ) list) (G:G) . 
     (EVERY (\b.b) 
       (MAP (\(l_,t_,Typ_). (Ty G t_ Typ_)) l_t_Typ_list))
    @@ -2237,7 +2240,7 @@ 

    12.3.2  Terms (in inductive defin For example, the rule

    gives rise to HOL code as below — note the list-lifted usage of the -is_v_of_t predicate, and the list appends (++) in the conclusion. +is_v_of_t predicate, and the list appends (++) in the conclusion.

    (* reduce_Rec *)  !(l'_t'_list:(label#t) list) 
            (l_v_list:(label#t) list) (l:label) (t:t) (t':t) . 
     ((EVERY (\(l_,v_). is_v_of_t v_) l_v_list) /\
    @@ -2248,7 +2251,7 @@ 

    12.3.2  Terms (in inductive defin

    For the Proj typing rule

    we need a specific projection (the -HOL EL) to +HOL EL) to pick out the j’th element:

    (* Ty_Proj *)  !(l_Typ_list:(label#Typ) list) 
            (j:index) (G:G) (t:t) . 
    @@ -2266,7 +2269,7 @@ 

    12.3.2  Terms (in inductive defin definitions to induct over them.

    For similar examples in Isabelle, the generated Isabelle for the first three rules of §12.1 is shown below (lightly hand-edited for format). The first involves an -Isabelle variable l_t_T_list, and list maps and projections +Isabelle variable l_t_T_list, and list maps and projections thereof.

    Ty_RcdI: "
       [|(formula_formuladots ((List.map (%(l_,t_,T_).( ( G , t_ , T_ ) : Ty)) l_t_T_list)))|] 
    @@ -2296,9 +2299,9 @@ 

    12.3.2  Terms (in inductive defin does not always build without change, in particular if tuples of size 3 or more are required in patterns.

    -

    12.3.3  List forms in homomorphisms

    Proof assistant homomorphisms in productions can refer to dot-form +

    12.3.3 List forms in homomorphisms

    Proof assistant homomorphisms in productions can refer to dot-form metavariables and nonterminals. For example, the second production -below (taken from test17.9) mentions [[x1 t1 ... xn tn]] in the isa +below (taken from test17.9) mentions [[x1 t1 ... xn tn]] in the isa homomorphism. This must exactly match the dot form in the production except that all terminals must be omitted — the metavariables and nonterminals must occur in the same order as in the production, and @@ -2340,22 +2343,22 @@

    12.3.3  List forms in homomorphis

    Note that in the second the list of pairs is projected out from the -x_t_K_list list of triples that is quantified over in the rule.

    +x_t_K_list list of triples that is quantified over in the rule.

    -

    13  Subrules

    Subrule declarations have the form +

    13 Subrules

    Subrule declarations have the form

       subrules
         nt1 <:: nt2
     

    -where nt1 and nt2 are nonterminal roots.

    Subrules can be chained, i.e. there can be a pair of +where nt1 and nt2 are nonterminal roots.

    Subrules can be chained, i.e. ‍there can be a pair of subrule declarations nt1 <:: nt2 and nt2 <:: nt3, -and they can form a directed acyclic graph, e.g. with +and they can form a directed acyclic graph, e.g. ‍with nt0 <:: nt1, nt0 <:: nt2, nt1 <:: nt3, and nt2 <:: nt3. However, there cannot be cycles, or nonterminal roots for which there are multiple upper bounds. Subrule declarations should not involve nonterminal roots for which proof-assistant type homs are specified.

    We support the case in which the upper rule is also -non-free, i.e. it contains productions that mention nonterminals that +non-free, i.e. ‍it contains productions that mention nonterminals that occur on the left of a subrule declaration. In the example below (test11.ott) the t rule contains a production Foo v. @@ -2393,7 +2396,7 @@

    13  Subrules

      axI: "[|is_t t ; is_v v|] ==>  ( t , v ) : Baz"
     
    -

    14  Context rules

    The system supports the definition of single-hole contexts, e.g. for +

    14 Context rules

    The system supports the definition of single-hole contexts, e.g. ‍for evaluation contexts. For example, suppose one has a term grammar as below:

     t :: ’t_’ ::=                               {{ com term    }}
    @@ -2410,7 +2413,7 @@ 

    14  Context rules

    {{ tex [[E]] \cdot [[t]] }}

    A context grammar is declared as a normal grammar but with a single -occurrence of the terminal __ in each production, e.g. as in +occurrence of the terminal __ in each production, e.g. as in the grammar for E below (a rather strange evaluation strategy, admittedly).

    @@ -2428,7 +2431,7 @@ 

    14  Context rules

    causes Ott to (a) check that each production of the E grammar is indeed a context for the t grammar, and (b) generates -proof assistant functions, e.g. appctx_E_t, to apply a context +proof assistant functions, e.g. ‍appctx_E_t, to apply a context to a term:

    (** context application *)
     Definition appctx_E_t (E5:E) (t_6:t) : t :=
    @@ -2443,7 +2446,7 @@ 

    14  Context rules

    Nested production shows, context productions can involve nested term structure.

    Note also that here the E grammar is not free (it mentions the subrule nonterminal v) so an isvalue predicate -is_E_of_E is also generated.

    In general, context rule declarations have the form +is_E_of_E is also generated.

    In general, context rule declarations have the form

       contextrules
         ntE _:: nt1 :: nt2
    @@ -2453,7 +2456,7 @@ 

    14  Context rules

    nt2 positions.

    Just as for substitutions, the context application function is typically used by adding a metaproduction to the term grammar. Here we add a production E.t to the t grammar with -an icho hom that uses appctx_E_t. +an icho hom that uses appctx_E_t.

     t :: ’t_’ ::=                               {{ com term    }}
       ...
    @@ -2467,13 +2470,13 @@ 

    14  Context rules

    -------------- :: ctx E.t --> E.t’

    One would typically also define a terminals production for -the hole terminal __, e.g. here we typeset the hole as [·]. +the hole terminal __, e.g. ‍here we typeset the hole as [·].

     terminals :: ’terminals_’ ::=
       | __           ::  :: hole    {{ tex [\cdot] }}
     
    -

    15  Auxiliary Rules

    +

    15 Auxiliary Rules

    We permit an aux hom on grammar rules. For any rule with such a hom, we transform that rule by appending an _aux to its primary nonterminal root name. We then add a synthesised rule with the original nonterminal @@ -2501,7 +2504,7 @@

    15  Auxiliary Rules

    -generate_aux_rules, which one might (eg) set to false for latex output and true for OCaml output.

    -

    16  Functions

    Ott includes experimental support for writing function definitions. +

    16 Functions

    Ott includes experimental support for writing function definitions. As a simple example, consider the Ott file below:

         grammar
          n :: ’n_’ ::=
    @@ -2515,7 +2518,7 @@ 

    16  Functions

    Ott by 0 + n2 === n2 S n1 + n2 === n1 + S n2 -

    Here the add function is compiled into the following Coq code:

    +

    Here the add function is compiled into the following Coq code:

         Fixpoint add (x1:num) (x2:num) : num:=
           match x1,x2 with
           | n_zero , n2 => n2
    @@ -2527,18 +2530,18 @@ 

    16  Functions

    Ott by declaration specifies:

    • -the name of the function: add -
    • the symbolic term that defines the lhs: n1 + n2 -
    • the non-terminal that defines the rhs: n +the name of the function: add +
    • the symbolic term that defines the lhs: n1 + n2 +
    • the non-terminal that defines the rhs: n

    The type of the arguments of the function is defined by the non-terminals appearing in the lhs, the return type by the rhs -non-terminal (so numnumnum in the +non-terminal (so numnumnum in the above example). As side-effect, whenever a function of type -symb_termnt is defined, a production -nt ::= symb_term is added to the definition of the -non-terminal nt (in the above example, the production -n1 + n2 is added to the grammar of num).

    Functions are then defined by case analysis, where the lhs and the rhs -are separated by the reserved symbol ===.

    The {{ hol-proof }} hom +symb_termnt is defined, a production +nt ::= symb_term is added to the definition of the +non-terminal nt (in the above example, the production +n1 + n2 is added to the grammar of num).

    Functions are then defined by case analysis, where the lhs and the rhs +are separated by the reserved symbol ===.

    The {{ hol-proof }} hom allows the specification of a termination proof, which is required by Hol. Mutually recursive functions can be defined in the same funs block, analogously to mutually recursive rule @@ -2546,10 +2549,10 @@

    16  Functions

    Ott different provers can result in a function definition being compiled correctly to one prover but not to others.

    -

    17  Parsing Priorities

    Symbolic terms that can have more than one parse tree are typically considered +

    17 Parsing Priorities

    Symbolic terms that can have more than one parse tree are typically considered erroneous; however, certain classes of parse trees are ignored in order to support common idioms that are ambiguous. For example, the production -

    +

    Γ ::= Γ1, .., Γn
    Γ ::= Γ1, .., Γn

    might be used to allow a list of typing contexts to be appended together, but it is highly ambiguous. The following restrictions forbid many unwanted parses @@ -2571,10 +2574,10 @@

    17  Parsing Priorities

    -

    18  Combining multiple source files

    +

    18 Combining multiple source files

    Ott can be invoked with multiple source files. Input filenames with extensions .tex, .v, .thy, .sml, or ml are simply copied into @@ -2663,55 +2666,55 @@

    18  Combining multiple source files

    15 shows the Ott source file for a +For example, Figure ‍15 shows the Ott source file for a let feature in isolation, taken from our Ott development of -some languages from Pierce’s TAPL [Pie02]. -


    -
    -
    
    -grammar
    -t :: Tm ::=                                        {{ com terms: }}
    -  | let x = t in t’                   ::   :: Let (+ bind x in t’ +)     {{ com let binding }}
    +some languages from Pierce’s TAPL ‍[].
    +


    +
    +
    
    +grammar
    +t :: Tm ::=                                        {{ com terms: }}
    +  | let x = t in t’                   ::   :: Let (+ bind x in t’ +)     {{ com let binding }}
     
     
    -defns 
    -Jop ::::= 
    +defns 
    +Jop ::::= 
     
    -defn 
    -t --> t’ :: :: red :: E_ {{ com Evaluation }} by
    +defn 
    +t --> t’ :: :: red :: E_ {{ com Evaluation }} by
     
    ------------------------------ :: LetV
    +----------------------------- :: LetV
     let x=v1 in t2 --> [x|->v1]t2 
     
     t1 --> t1’
    ----------------------------------- :: Let
    +---------------------------------- :: Let
     let x=t1 in t2 --> let x=t1’ in t2
     
     
    -defns
    -Jtype ::::= 
    +defns
    +Jtype ::::= 
     
    -defn
    -G |- t : T :: :: typing :: T_ {{ com Typing }} by
    +defn
    +G |- t : T :: :: typing :: T_ {{ com Typing }} by
     
     G |- t1:T1
     G,x:T1 |- t2:T2
    ------------------------- :: Let
    +------------------------ :: Let
     G |- let x=t1 in t2 : T2
    -
    -
    +
    +
    Figure 15: An ott source file for the let fragment of TAPL
    -

    +


    The original TAPL languages were produced using -TinkerType [LP03] to compose features and check for +TinkerType ‍[] to compose features and check for conflicts. In examples/tapl we build a system, similar to the TinkerType sys-fullsimple, from ott source files that correspond roughly to the various TinkerType components, each with syntax and semantic rules for a single feature.

    -

    19  Hom blocks

    +

    19 Hom blocks

    Bindspecs and homomorphisms for productions, and any homomorphisms for definitions, can appear in an Ott source file either attached to the production or definition, as we have shown earlier, or @@ -2733,89 +2736,89 @@

    19  Hom blocks

    Each of these begins with a prefix and then has a sequence of production name or definition name kernels, each followed by a -sequence of bindspecs and then a sequence of homomorphisms.

    The test10_homs.ott example, in Fig. 16, shows this. It -is semantically equivalent to the test10.ott example of -Fig. 8, but the homs have been moved into hom +sequence of bindspecs and then a sequence of homomorphisms.

    The test10_homs.ott example, in Fig. ‍16, shows this. It +is semantically equivalent to the test10.ott example of +Fig. ‍8, but the homs have been moved into hom blocks. -


    -
    -
    
    -metavar termvar , x ::= 
    -  {{ isa string }} {{ coq nat }} {{ coq-equality }} {{ hol string }} {{ lex alphanum }}
    -  {{ tex \mathit{[[termvar]]} }} {{ com  term variable  }} 
    +


    +
    +
    +metavar termvar , x ::= 
    +  {{ isa string }} {{ coq nat }} {{ coq-equality }} {{ hol string }} {{ lex alphanum }}
    +  {{ tex \mathit{[[termvar]]} }} {{ com  term variable  }} 
     
    -grammar
    -  t :: ’t_’ ::=    {{ com term }}
    -    | x                   ::   :: Var   
    -    | \ x . t             ::   :: Lam  
    -    | t t’                ::   :: App  
    -    | ( t )               :: S :: paren 
    -    | { t / x } t’        :: M :: tsub  
    +grammar
    +  t :: ’t_’ ::=    {{ com term }}
    +    | x                   ::   :: Var   
    +    | \ x . t             ::   :: Lam  
    +    | t t’                ::   :: App  
    +    | ( t )               :: S :: paren 
    +    | { t / x } t’        :: M :: tsub  
     
    -  v :: ’v_’ ::=    {{ com  value }}
    -    | \ x . t             ::   :: Lam   
    +  v :: ’v_’ ::=    {{ com  value }}
    +    | \ x . t             ::   :: Lam   
     
    -  terminals :: ’terminals_’ ::=
    -    | \                   ::   :: lambda  {{ tex \lambda }}
    -    | -->                 ::   :: red     {{ tex \longrightarrow }}
    +  terminals :: ’terminals_’ ::=
    +    | \                   ::   :: lambda  {{ tex \lambda }}
    +    | -->                 ::   :: red     {{ tex \longrightarrow }}
     
     
    -homs ’t_’
    -  :: Lam  (+ bind x in t +)  
    +homs ’t_’
    +  :: Lam  (+ bind x in t +)  
     
    -homs ’t_’
    -  :: Var     {{ com variable }}         
    -  :: Lam     {{ com abstraction }}      
    -  :: App     {{ com application }}      
    -  :: paren   {{ ich [[t]] }} 
    -  :: tsub    {{ ich ( tsubst_t [[t]] [[x]] [[t’]] ) }}
    +homs ’t_’
    +  :: Var     {{ com variable }}         
    +  :: Lam     {{ com abstraction }}      
    +  :: App     {{ com application }}      
    +  :: paren   {{ ich [[t]] }} 
    +  :: tsub    {{ ich ( tsubst_t [[t]] [[x]] [[t’]] ) }}
     
    -homs ”
    -  :: reduce  {{ com $[[t1]]$ reduces to $[[t2]]$ }} 
    +homs ”
    +  :: reduce  {{ com $[[t1]]$ reduces to $[[t2]]$ }} 
     
     
    -subrules
    -  v <:: t
    +subrules
    +  v <:: t
     
    -substitutions
    -  single t x :: tsubst 
    +substitutions
    +  single t x :: tsubst 
     
    -defns
    -  Jop ::::=
    +defns
    +  Jop ::::=
     
    -    defn
    -    t1 --> t2 ::  :: reduce ::by 
    +    defn
    +    t1 --> t2 ::  :: reduce ::by 
     
     
    -    --------------------------  :: ax_app
    -    (\x.t12) v2 -->  {v2/x}t12
    +    --------------------------  :: ax_app
    +    (\x.t12) v2 -->  {v2/x}t12
     
         t1 --> t1’
    -    -------------- :: ctx_app_fun
    +    -------------- :: ctx_app_fun
         t1 t --> t1’ t
     
         t1 --> t1’
    -    -------------- :: ctx_app_arg
    +    -------------- :: ctx_app_arg
         v t1 --> v t1’
     
    -
    +
    -
    +
    Figure 16: Hom Sections: test10_homs.ott
    -

    +

    -

    20  Isabelle syntax support

    Ott has limited facilities to allow the Isabelle mixfix syntax support -and xsymbol to be used. The example test10_isasyn.ott shows +

    20 Isabelle syntax support

    Ott has limited facilities to allow the Isabelle mixfix syntax support +and xsymbol to be used. The example test10_isasyn.ott shows this in use.

    Non-meta productions can be annotated with isasyn and/or isaprec homomorphisms. -For example, test10_isasyn.ott contains the production +For example, test10_isasyn.ott contains the production

       | t t’      :: :: App   {{ isasyn [[t]]\<bullet>[[t’]] }}  {{ isaprec 50 }}
     

    The two homs are used to output the Isabelle syntax annotation in the -t_App clause of the datatype definition below. +t_App clause of the datatype definition below.

    t = 
        t_Var "termvar"  
      | t_Lam "termvar" "t"  ("\<lambda> _ . _" 60)
    @@ -2823,12 +2826,12 @@ 

    20  Isabelle syntax support

    -

    21  Isabelle code generation example

    The Isabelle/Coq/HOL code generation facilities can be sometimes used to +

    21 Isabelle code generation example

    The Isabelle/Coq/HOL code generation facilities can be sometimes used to generate (variously) OCaml and SML code from the Isabelle/Coq/HOL -definitions produced by Ott.

    For example, the test10st_codegen.thy file uses Isabelle +definitions produced by Ott.

    For example, the test10st_codegen.thy file uses Isabelle code generation to produce SML code to calculate the possible -reductions of terms in the test10st.ott simply typed lambda +reductions of terms in the test10st.ott simply typed lambda calculus.

    theory test10st_codegen 
     imports test10st_snapshot_out Executable_Set
     begin
    @@ -2910,7 +2913,7 @@ 

    21  Isabelle code generation example -

    22  Reference: Command-line usage

    A good place to get started is one of the test +

    22 Reference: Command-line usage

    A good place to get started is one of the test make targets in the ott directory, e.g.

    test10: tests/test10.ott
                bin/ott                                                \
    @@ -2920,23 +2923,23 @@ 

    22  Reference: Command-line usage

    < -parse ":t: (\z.z z) y" \ && ($(LATEX) out; $(DVIPS) out -o)

    -When make test10 is executed, ott: +When make test10 is executed, ott:

    • -reads the source file tests/test10.ott +reads the source file tests/test10.ott
    • (if one also specifies -show_sort true and -show_defns true) prints on standard output various diagnostic information, including ASCII versions of the grammar and inductive definitions. By default these are coloured (using -vt220 control codes) with metavariables in red, nonterminals +vt220 control codes) with metavariables in red, nonterminals in yellow, terminals in green, and object variables in white. Scanning over this output quickly picks up some common errors. -
    • parses the symbolic term (\z.z z) y using the t +
    • parses the symbolic term (\z.z z) y using the t grammar and prints the result to standard output -
    • generates Isabelle definitions in the file out.thy -
    • generates Coq definitions in the file out.v -
    • generates HOL definitions in the file outScript.sml -
    • generates a LaTeX document in the file out.tex, with a +
    • generates Isabelle definitions in the file out.thy +
    • generates Coq definitions in the file out.v +
    • generates HOL definitions in the file outScript.sml +
    • generates a LaTeX document in the file out.tex, with a standard document preamble to make it self-contained.

    That LaTeX document is then compiled and converted to postscript.

    For convenience, input files can also be listed at the end of the command line: @@ -2944,14 +2947,14 @@

    22  Reference: Command-line usage

    <

    is equivalent to

      ott -i <file1> ... -i <filen> [options]
    -

    The %.out Makefile target runs ott with common -defaults on the file %.ott, so for example executing make tests/test10.out -runs ott on tests/test10.ott, generating all outputs. There are -also targets  %.coq.out,  %.hol.out, and - %.isa.out, to generate just LaTeX and the code for one proof -assistant, and  %.tex.out, to generate just LaTeX.

    The ott command-line options (with default values where applicable) are +

    The ‍%.out Makefile target runs ott with common +defaults on the file ‍%.ott, so for example executing make tests/test10.out +runs ott on tests/test10.ott, generating all outputs. There are +also targets  ‍%.coq.out,  ‍%.hol.out, and + ‍%.isa.out, to generate just LaTeX and the code for one proof +assistant, and  ‍%.tex.out, to generate just LaTeX.

    The ott command-line options (with default values where applicable) are shown below. -

    Ott version 0.32   distribution of Wed 9 Mar 16:05:28 GMT 2022
    +

    Ott version 0.33   distribution of Mon 16 Jan 15:32:01 GMT 2023
     
     usage: ott <options> <filename1> .. <filenamen> 
       (use "OCAMLRUNPARAM=p  ott ..." to show the ocamlyacc trace)
    @@ -3016,71 +3019,71 @@ 

    22  Reference: Command-line usage

    < --help Display this list of options
    -

    23  Reference: The language of symbolic terms

    A syntax definition conceptually defines two different languages: that +

    23 Reference: The language of symbolic terms

    A syntax definition conceptually defines two different languages: that of concrete terms of the object language, and that of symbolic terms over the object language. -The former includes concrete variables (if nontrivial lex homs +The former includes concrete variables (if nontrivial lex homs have been specified for metavariables). The latter includes the former but also allows symbolic metavariables and nonterminals. Symbolic terms may also include the production-name annotations mentioned in §3. -For a syntax definition with list forms (c.f. §12) symbolic +For a syntax definition with list forms (c.f. ‍§12) symbolic terms also include various list constructs. A simplified abstract syntax of symbolic terms is shown in -Figure 17, omitting list forms. In this section we give an +Figure ‍17, omitting list forms. In this section we give an informal definition of the full concrete syntax of symbolic terms. -


    +


    Figure 17: Mini-Ott in Ott: symbolic terms
    -

    The premises and conclusions of inductive definition rules are +


    The premises and conclusions of inductive definition rules are symbolic terms. -The language of symbolic terms is defined informally below, with interpretation functions [[_]] that map defined entities into grammar clauses.

    For a rule rule   = -

    +The language of symbolic terms is defined informally below, with interpretation functions [[_]] that map defined entities into grammar clauses.

    For a rule rule   = +

    nontermroot1   ,   ..   ,   nontermrootn ::   ’’   ::=   prod1   ..   prodm 
    nontermroot1   ,   ..   ,   nontermrootn::   ’’   ::=   prod1   ..   prodm

    we have -

    - - - -
     [[rule]]::= 
     |nontermroot suffix(1) 
     |[[prod1]] 
     |..  
     |[[prodm]]  +

    + + + +
     [[rule]]::= 
     |nontermrootsuffix(1) 
     |[[prod1]] 
     |..  
     |[[prodm]] 

    (1) for each nontermroot in the set -{nontermroot1   ,   ..   ,   +{nontermroot1   ,   ..   ,   nontermrootn } and for each nontermroot defined by any rule′ -which is declared as a subrule of this rule.

    For a production prod   = -

    +which is declared as a subrule of this rule.

    For a production prod   = +

    |   element1   ..   elementm   ::   ::   prodname
    |   element1   ..   elementm   ::   ::   prodname

    we have -

    - -
     [[prod]]::= 
     |[[element1]]   ..   [[elementm]] 
     |:prodname:  [[element1]]   ..   [[elementm]]  +

    + +
     [[prod]]::= 
     |[[element1]]   ..   [[elementm]] 
     |:prodname:  [[element1]]   ..   [[elementm]] 

    For an element there are various cases.

    1. For a terminal terminal -
      +
       [[terminal]]::=terminal
       [[terminal]]::=terminal
      -
    2. For a nonterminal nontermroot   suffix -
       [[nontermroot   suffix]]::=[[rule]]  +
    3. For a nonterminal nontermrootsuffix +
       [[nontermroot   suffix]]::=[[rule]] 
      where rule includes nontermroot among the nonterminal roots it defines. (Note that this does not depend on what suffix was used in the grammar, and similarly for the metavar case below.)
    4. For an index variable indexvarroot -
       [[indexvarroot]]::=indexvarroot′ +
       [[indexvarroot]]::=indexvarroot
      for each indexvarroot′ defined by the indexvar -definition that defines indexvarroot.
    5. For a metavariable metavarroot   suffix -
      - - +definition that defines indexvarroot.
    6. For a metavariable metavarrootsuffix +
    7.  [[metavarroot   suffix]]::= 
       |metavarroot′ suffix(1) 
       |variable 
      + +
       [[metavarroot   suffix]]::= 
       |metavarroot′ suffix(1) 
       |variable
      (1) for each metavarroot′ defined by the metavar @@ -3091,79 +3094,79 @@

      23  Reference: The language of symbolic which can be parsed as a nonterminal, metavariable or terminal of the syntax definition.
    8. A list form element element could be any of the following, either without a separating terminal: -
      element1 .. elementn  -  dots   -element1 .. elementn  +
      - - - -
      element1 .. elementn  +  dots   +element1 .. elementn
      </  element1 .. elementn   //   indexvar   />  +
      </  element1 .. elementn   //   indexvar   />
      </  element1 .. elementn   //   indexvar   IN   indexvar′   />  +
      </  element1 .. elementn   //   indexvar   IN   indexvar′   />
      </  element1 .. elementn   //   indexvar   IN   number   dots   indexvar′   />  +
      </  element1 .. elementn   //   indexvar   IN   number   dots   indexvar′   />
      </  element1 .. elementn   //   indexvar   IN   number   dots   indexvar-1   />  +
      </  element1 .. elementn   //   indexvar   IN   number   dots   indexvar-1   />
      or with a separating terminal: -
      element1 .. elementn  -  terminal   dots   terminal   -element1 .. elementn  +
      - - - -
      element1 .. elementn  +  terminal   dots   terminal   +element1 .. elementn
      </  element1 .. elementn   //  terminal //  indexvar   />  +
      </  element1 .. elementn   //  terminal//  indexvar   />
      </  element1 .. elementn   //  terminal //  indexvar   IN   indexvar′   />  +
      </  element1 .. elementn   //  terminal//  indexvar   IN   indexvar′   />
      </  element1 .. elementn   //  terminal //   indexvar   IN   number   dots   indexvar′   />  +
      </  element1 .. elementn   //  terminal//   indexvar   IN   number   dots   indexvar′   />
      </  element1 .. elementn   //  terminal //   indexvar   IN   number   dots   indexvar-1   />  +
      </  element1 .. elementn   //  terminal//   indexvar   IN   number   dots   indexvar-1   />
      In any of these cases the interpretation [[element]] is the lists (separated by the terminal if one was specified) of concrete list entries and of list forms. Without a separating terminal, this is: -
      - - - +
       [[element]]::=(concrete_list_entry | list_form)*(2), (3)
       
      concrete_list_entry::=[[element1]]   ..   [[elementn]] 
       
      + + + - - + - - -
       [[element]]::=(concrete_list_entry | list_form)*(2), (3)
      concrete_list_entry::=[[element1]]   ..   [[elementn]] 
      list_form::= 
       |[[element1]]   ..   [[elementn]]  - dots′  -[[element1]]   ..   [[elementn]](1) 
       |</  [[element1]] .. [[elementn]]   //  indexvar″   />  +
       |[[element1]]   ..   [[elementn]]  + dots′  +[[element1]]   ..   [[elementn]](1) 
       |</  [[element1]] .. [[elementn]]   //  indexvar″   />
       |</  [[element1]] .. [[elementn]] //  indexvar″   IN   indexvar‴   />  +
       |</  [[element1]] .. [[elementn]] //  indexvar″   IN   indexvar‴   />
       |</  [[element1]] .. [[elementn]] //   indexvar″   IN   number′   dots′   indexvar‴   />  +
       |</  [[element1]] .. [[elementn]] //   indexvar″   IN   number′   dots′   indexvar‴   />
       |</  [[element1]] .. [[elementn]] //   indexvar″   IN   number′   dots′   indexvar-1   />  +
       |</  [[element1]] .. [[elementn]] //   indexvar″   IN   number′   dots′   indexvar-1   />
      This is subject to constraints: (1) that -[[element1]]   ..   [[elementn]] +[[element1]]   ..   [[elementn]] and -[[element1]]   ..   [[elementn]] can be +[[element1]]   ..   [[elementn]] can be anti-unified with exactly one varying index; (2) if the list has only concrete entries (i.e., no list forms), its length must meet the constraint of any dots in the element.

      With a separating terminal, we have: -

       [[element]]::=є | (concrete_list_entry | list_form) -(terminal (concrete_list_entry | list_form))* +

       [[element]]::=є | (concrete_list_entry | list_form) +(terminal (concrete_list_entry | list_form))*

      In the above -

      - - +

      dots::=.. | ... | .... 
      number::=0 | 1  
      suffix::=suffix_item* 
      -
      + + - - - - - + + + + +
      dots::=.. | ... | ....
      number::=0 | 1  
      suffix::=suffix_item*
      suffix_item::= 
       |(0 | 1 |2 | 3 |4 | 5 |6 | 7 |8 | 9)+(longest match) 
       |_ 
       | 
       |indexvar 
       |indexvar-1 
       |(0 | 1 |2 | 3 |4 | 5 |6 | 7 |8 | 9)+(longest match) 
       |_
       |
       |indexvar
       |indexvar-1

      Further, whitespace (' '|'\010'|'\009'|'\013'|'\012') is allowed before any token except a those in a suffix, and nonterminals, metavariables, index variables, and terminals that end with an alphanumeric character, must not be followed by an alphanumeric character.

      The tool also builds a parser +

      Further, whitespace (' '|'\010'|'\009'|'\013'|'\012') is allowed before any token except a those in a suffix, and nonterminals, metavariables, index variables, and terminals that end with an alphanumeric character, must not be followed by an alphanumeric character.

      The tool also builds a parser for concrete terms, with fake nonterminal roots concrete_ntr for each primary ntr of the syntax definition. One can switch to concrete-term parsing with a :concrete: annotation, @@ -3171,7 +3174,7 @@

      23  Reference: The language of symbolic

      \[ [[ :concrete: \Z1<:Top. \x:Z1.x ]]\]
       

      -shown in Figure 10. Below such an annotation, only concrete terms +shown in Figure ‍10. Below such an annotation, only concrete terms are permitted, with no further annotation, no symbolic nonterminals or metavariables, no list dot forms or comprehensions, etc.

      Parsing of terms is done with a scannerless GLR parser over character-list inputs. The parser @@ -3188,23 +3191,23 @@

      23  Reference: The language of symbolic typical in semantic rules. Its performance on large (whole-program size) examples is untested.

      -

      24  Reference: Generation of proof assistant definitions

      This section briefly summarises the steps involved in the generation -of proof assistant definitions from an Ott source file. For a description of the locally-nameless backend, refer to http://moscova.inria.fr/ zappa/projects/ln_ott/. +

      24 Reference: Generation of proof assistant definitions

      This section briefly summarises the steps involved in the generation +of proof assistant definitions from an Ott source file. For a description of the locally-nameless backend, refer to http://moscova.inria.fr/ ‍zappa/projects/ln_ott/.

      -

      24.1  Generation of types