Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file Rich_string.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118includeSpecsmoduleMake(Enricher:ENRICHER):TYPEwithmoduleEnricher=Enricher=structmoduleEnricher=Enricher(* This implementation optimizes for the case of joining a
list of rich strings together over simple, pairwise
concatenation as the former is much more prevalent.
As such, ( ++ ) is optimized to reuse lists and keep the
structure flat as much as possible. Tests must (and will)
make sure that monoidal laws are preserved. *)typet=|Empty|Stringofstring|EnrichedofEnricher.t*t|Joinoft*tlistletrec(=)rs1rs2=match(rs1,rs2)with|(Empty,Empty)->true|(Strings1,Strings2)->String.equals1s2|(Enriched(e1,rs1),Enriched(e2,rs2))->Enricher.(e1=e2)&&rs1=rs2|(Join(sep1,rss1),Join(sep2,rss2))->sep1=sep2&&List.for_all2(=)rss1rss2|(_,_)->falseletempty=Emptyletstrings=Stringsletenrichers=Enriched(e,rs)letjoin?on:(sep=String" ")rss=Join(sep,rss)letflattenrss=join~on:Emptyrssletjoin_linesrss=join~on:(String"\n")rss(* depending on the output, concatenation will sometimes
perform optimizations to produce structures as flat as
possible. *)letrecdecide_concat_optimizationrs1rs2=match(rs1,rs2)with(* left identity *)|(Empty,_)->Somers2(* right identity *)|(_,Empty)->Somers1(* first optimization: if rs1 and rs2 are both lists
joined by the same separator, we can simply concatenate
both lists together, which avoids ending with many
nested Joins of only two elements. *)|(Join(sep1,rss1),Join(sep2,rss2))whensep1=sep2->Some(Join(sep1,optimize_list(rss1@rss2)))(* second optimization: if rs2 is an empty-joined list of
rich strings, we can simply cons rs1 to it for the same
reason as the first optimization. *)|(_,Join(Empty,rss))->Some(Join(Empty,optimize_list(rs1::rss)))(* to preserve associativity, we must perform the above
optimization on the left handside as well, even though
it is less efficient. *)|(Join(Empty,rss),_)->Some(Join(Empty,optimize_list(rss@[rs2])))(* third optimization: concatenating two strings that have
the same enrichment results in reusing the enrichment
and concatenating the underlying rich strings instead. *)|(Enriched(e1,rs1),Enriched(e2,rs2))whenEnricher.(e1=e2)->Some(Enriched(e1,rs1++rs2))(* IMPORTANT: do NOT do any optimizations on [String]!
concatenating the underlying strings BREAK ASSOCIATIVITY
and are not even worth it performance-wise! *)|(_,_)->Noneand(++)rs1rs2=matchdecide_concat_optimizationrs1rs2with|Somers->rs|None->flatten[rs1;rs2]andcons_optimizedcurrentprocessed=matchprocessedwith|[]->current::[]|first::rest->(matchdecide_concat_optimizationcurrentfirstwith|Somers->rs::rest|None->current::processed)andoptimize_list(rss:tlist)=List.(fold_rightcons_optimizedrss[])letrecis_empty=function|Empty|Join(_,[])->true|String_->false|Enriched(_,rs)->is_emptyrs|Join(sep,rss)->is_emptysep&&List.for_allis_emptyrssletrecis_enriched=function|Empty|String_->false|Enriched_->true|Join(sep,rss)->is_enrichedsep||List.existsis_enrichedrssletrecrender=function|Empty->""|Strings->s|Enriched(e,rs)->Enricher.enriche(renderrs)|Join(sep,rss)->rss|>List.filter(Fun.negateis_empty)|>List.maprender|>String.concat(rendersep)letprint?(out=stdout)?(ending=Some(String"\n"))rs=letrendered_ending=ending|>Option.maprender|>Option.value~default:""inPrintf.fprintfout"%s%s"(renderrs)rendered_endingend