123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499(* This file is part of the Catala compiler, a specification language for tax
and social benefits computation rules. Copyright (C) 2020 Inria,
contributors: Denis Merigoux <denis.merigoux@inria.fr>, Emile Rolley
<emile.rolley@tuta.io>
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License. *)typebackend_lang=En|Fr|Pltypebackend_option_builtin=[`Latex|`Makefile|`Html|`Interpret|`Typecheck|`OCaml|`Python|`Scalc|`Lcalc|`Dcalc|`Scopelang|`Proof]type'abackend_option=[backend_option_builtin|`Pluginof'a]letbackend_option_to_string=function|`Interpret->"Interpret"|`Makefile->"Makefile"|`OCaml->"Ocaml"|`Scopelang->"Scopelang"|`Dcalc->"Dcalc"|`Latex->"Latex"|`Proof->"Proof"|`Html->"Html"|`Python->"Python"|`Typecheck->"Typecheck"|`Scalc->"Scalc"|`Lcalc->"Lcalc"|`Plugins->sletbackend_option_of_stringbackend=matchString.lowercase_asciibackendwith|"interpret"->`Interpret|"makefile"->`Makefile|"ocaml"->`OCaml|"scopelang"->`Scopelang|"dcalc"->`Dcalc|"latex"->`Latex|"proof"->`Proof|"html"->`Html|"python"->`Python|"typecheck"->`Typecheck|"scalc"->`Scalc|"lcalc"->`Lcalc|s->`Plugins(** Source files to be compiled *)letsource_files:stringlistref=ref[]letlocale_lang:backend_langref=refEnletcontents:stringref=ref""(** Prints debug information *)letdebug_flag=reffalse(* Styles the terminal output *)letstyle_flag=reftrue(* Max number of digits to show for decimal results *)letmax_prec_digits=ref20lettrace_flag=reffalseletoptimize_flag=reffalseletdisable_counterexamples=reffalseletavoid_exceptions_flag=reffalseopenCmdlinerletfile=Arg.(required&pos1(somefile)None&info[]~docv:"FILE"~doc:"Catala master file to be compiled.")letdebug=Arg.(value&flag&info["debug";"d"]~doc:"Prints debug information.")typewhen_enum=Auto|Always|Neverletwhen_opt=Arg.enum["auto",Auto;"always",Always;"never",Never]letcolor=Arg.(value&opt~vopt:Alwayswhen_optAuto&info["color"]~doc:"Allow output of colored and styled text. If set to $(i,auto), \
enabled when the standard output is to a terminal.")letunstyled=Arg.(value&flag&info["unstyled";"u"]~doc:"Removes styling (colors, etc.) from terminal output. Equivalent to \
$(b,--color=never)")letoptimize=Arg.(value&flag&info["optimize";"O"]~doc:"Run compiler optimizations.")lettrace_opt=Arg.(value&flag&info["trace";"t"]~doc:"Displays a trace of the interpreter's computation or generates \
logging instructions in translate programs.")letavoid_exceptions=Arg.(value&flag&info["avoid_exceptions"]~doc:"Compiles the default calculus without exceptions")letclosure_conversion=Arg.(value&flag&info["closure_conversion"]~doc:"Performs closure conversion on the lambda calculus")letwrap_weaved_output=Arg.(value&flag&info["wrap";"w"]~doc:"Wraps literate programming output with a minimal preamble.")letprint_only_law=Arg.(value&flag&info["print_only_law"]~doc:"In literate programming output, skip all code and metadata sections \
and print only the text of the law.")letbackend=Arg.(required&pos0(somestring)None&info[]~docv:"COMMAND"~doc:"Backend selection (see the list of commands for available options).")letplugins_dirs=letdoc="Set the given directory to be searched for backend plugins."inletenv=Cmd.Env.info"CATALA_PLUGINS"~docinletdefault=let(/)=Filename.concatin[Filename.dirnameSys.executable_name/Filename.parent_dir_name/"lib"/"catala"/"plugins";]inArg.(value&opt_alldirdefault&info["plugin-dir"]~docv:"DIR"~env~doc)letlanguage=Arg.(value&opt(somestring)None&info["l";"language"]~docv:"LANG"~doc:"Input language among: en, fr, pl.")letmax_prec_digits_opt=Arg.(value&opt(someint)None&info["p";"max_digits_printed"]~docv:"DIGITS"~doc:"Maximum number of significant digits printed for decimal results \
(default 20).")letdisable_counterexamples_opt=Arg.(value&flag&info["disable_counterexamples"]~doc:"Disables the search for counterexamples in proof mode. Useful when \
you want a deterministic output from the Catala compiler, since \
provers can have some randomness in them.")letex_scope=Arg.(value&opt(somestring)None&info["s";"scope"]~docv:"SCOPE"~doc:"Scope to be focused on.")letoutput=Arg.(value&opt(somestring)None&info["output";"o"]~docv:"OUTPUT"~doc:"$(i, OUTPUT) is the file that will contain the output of the \
compiler. Defaults to $(i,FILE).$(i,EXT) where $(i,EXT) depends on \
the chosen backend. Use $(b,-o -) for stdout.")typeoptions={debug:bool;color:when_enum;wrap_weaved_output:bool;avoid_exceptions:bool;backend:string;plugins_dirs:stringlist;language:stringoption;max_prec_digits:intoption;trace:bool;disable_counterexamples:bool;optimize:bool;ex_scope:stringoption;output_file:stringoption;closure_conversion:bool;print_only_law:bool;}letoptions=letmakedebugcolorunstyledwrap_weaved_outputavoid_exceptionsclosure_conversionbackendplugins_dirslanguagemax_prec_digitstracedisable_counterexamplesoptimizeex_scopeoutput_fileprint_only_law:options={debug;color=(ifunstyledthenNeverelsecolor);wrap_weaved_output;avoid_exceptions;backend;plugins_dirs;language;max_prec_digits;trace;disable_counterexamples;optimize;ex_scope;output_file;closure_conversion;print_only_law;}inTerm.(constmake$debug$color$unstyled$wrap_weaved_output$avoid_exceptions$closure_conversion$backend$plugins_dirs$language$max_prec_digits_opt$trace_opt$disable_counterexamples_opt$optimize$ex_scope$output$print_only_law)letcatala_tf=Term.(constf$file$options)letset_option_globalsoptions:unit=debug_flag:=options.debug;(style_flag:=matchoptions.colorwith|Always->true|Never->false|Auto->Unix.isattyUnix.stdout);trace_flag:=options.trace;optimize_flag:=options.optimize;disable_counterexamples:=options.disable_counterexamples;avoid_exceptions_flag:=options.avoid_exceptionsletversion="0.8.0"letinfo=letdoc="Compiler for Catala, a specification language for tax and social benefits \
computation rules."inletman=[`SManpage.s_description;`P"Catala is a domain-specific language for deriving \
faithful-by-construction algorithms from legislative texts.";`SManpage.s_commands;`I("$(b,Intepret)","Runs the interpreter on the Catala program, executing the scope \
specified by the $(b,-s) option assuming no additional external \
inputs.");`I("$(b,Typecheck)","Parses and typechecks a Catala program, without interpreting it.");`I("$(b,Proof)","Generates and proves verification conditions about the well-behaved \
execution of the Catala program.");`I("$(b,OCaml)","Generates an OCaml translation of the Catala program.");`I("$(b,Python)","Generates a Python translation of the Catala program.");`I("$(b,LaTeX)","Weaves a LaTeX literate programming output of the Catala program.");`I("$(b,HTML)","Weaves an HTML literate programming output of the Catala program.");`I("$(b,Makefile)","Generates a Makefile-compatible list of the file dependencies of a \
Catala program.");`I("$(b,Scopelang)","Prints a debugging verbatim of the scope language intermediate \
representation of the Catala program. Use the $(b,-s) option to \
restrict the output to a particular scope.");`I("$(b,Dcalc)","Prints a debugging verbatim of the default calculus intermediate \
representation of the Catala program. Use the $(b,-s) option to \
restrict the output to a particular scope.");`I("$(b,Lcalc)","Prints a debugging verbatim of the lambda calculus intermediate \
representation of the Catala program. Use the $(b,-s) option to \
restrict the output to a particular scope.");`I("$(b,Scalc)","Prints a debugging verbatim of the statement calculus intermediate \
representation of the Catala program. Use the $(b,-s) option to \
restrict the output to a particular scope.");`SManpage.s_authors;`P"The authors are listed by alphabetical order.";`P"Nicolas Chataing <nicolas.chataing@ens.fr>";`P"Alain Delaët-Tixeuil <alain.delaet--tixeuil@inria.fr>";`P"Aymeric Fromherz <aymeric.fromherz@inria.fr>";`P"Louis Gesbert <louis.gesbert@ocamlpro.com>";`P"Denis Merigoux <denis.merigoux@inria.fr>";`P"Emile Rolley <erolley@tutamail.com>";`SManpage.s_examples;`Pre"catala Interpret -s Foo file.catala_en";`Pre"catala Ocaml -o target/file.ml file.catala_en";`SManpage.s_bugs;`P"Please file bug reports at https://github.com/CatalaLang/catala/issues";]inletexits=Cmd.Exit.defaults@[Cmd.Exit.info~doc:"on error."1]inCmd.info"catala"~version~doc~exits~man(**{1 Terminal formatting}*)(**{2 Markers}*)lettime:floatref=ref(Unix.gettimeofday())letwith_style(styles:ANSITerminal.stylelist)(str:('a,unit,string)format)=if!style_flagthenANSITerminal.sprintfstylesstrelsePrintf.sprintfstrletformat_with_style(styles:ANSITerminal.stylelist)fmt(str:string)=if!style_flagthenFormat.pp_print_asfmt(String.lengthstr)(ANSITerminal.sprintfstyles"%s"str)elseFormat.pp_print_stringfmtstrletcall_unstyledf=letprev=!style_flaginstyle_flag:=false;letres=f()instyle_flag:=prev;reslettime_marker()=letnew_time=Unix.gettimeofday()inletold_time=!timeintime:=new_time;letdelta=(new_time-.old_time)*.1000.inifdelta>50.thenPrintf.printf"%s"(with_style[ANSITerminal.Bold;ANSITerminal.black]"[TIME] %.0f ms\n"delta)(** Prints [\[DEBUG\]] in purple on the terminal standard output *)letdebug_marker()=time_marker();with_style[ANSITerminal.Bold;ANSITerminal.magenta]"[DEBUG] "(** Prints [\[ERROR\]] in red on the terminal error output *)leterror_marker()=with_style[ANSITerminal.Bold;ANSITerminal.red]"[ERROR] "(** Prints [\[WARNING\]] in yellow on the terminal standard output *)letwarning_marker()=with_style[ANSITerminal.Bold;ANSITerminal.yellow]"[WARNING] "(** Prints [\[RESULT\]] in green on the terminal standard output *)letresult_marker()=with_style[ANSITerminal.Bold;ANSITerminal.green]"[RESULT] "(** Prints [\[LOG\]] in red on the terminal error output *)letlog_marker()=with_style[ANSITerminal.Bold;ANSITerminal.black]"[LOG] "(**{2 Printers}*)(** All the printers below print their argument after the correct marker *)letconcat_with_line_depending_prefix_and_suffix(prefix:int->string)(suffix:int->string)(ss:stringlist)=matchsswith|[]->prefix0|_::_->letlen=List.lengthssinletsuffixi=ifi<len-1thensuffixielse""inString.concat""@@List.concat@@List.mapi(funis->[prefixi;(ifs=""then""else" ");s;suffixi])ss(** The int argument of the prefix corresponds to the line number, starting at 0 *)letadd_prefix_to_each_line(s:string)(prefix:int->string)=concat_with_line_depending_prefix_and_suffix(funi->prefixi)(fun_->"\n")(String.split_on_char'\n's)letdebug_print(format:('a,out_channel,unit)format)=if!debug_flagthenPrintf.printf("%s"^^format^^"\n%!")(debug_marker())elsePrintf.ifprintfstdoutformatletdebug_format(format:('a,Format.formatter,unit)format)=if!debug_flagthenFormat.printf("%s@[<hov>"^^format^^"@]@.")(debug_marker())elseFormat.ifprintfFormat.std_formatterformatleterror_print(format:('a,out_channel,unit)format)=Printf.eprintf("%s"^^format^^"\n%!")(error_marker())letwarning_print(format:('a,out_channel,unit)format)=Printf.printf("%s"^^format^^"\n%!")(warning_marker())letresult_print(format:('a,out_channel,unit)format)=Printf.printf("%s"^^format^^"\n%!")(result_marker())letresult_format(format:('a,Format.formatter,unit)format)=Format.printf("%s"^^format^^"\n%!")(result_marker())letlog_print(format:('a,out_channel,unit)format)=Printf.printf("%s"^^format^^"\n%!")(log_marker())letlog_format(format:('a,Format.formatter,unit)format)=Format.printf("%s@[<hov>"^^format^^"@]@.")(log_marker())