123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145openEngineopenEnvironmentopenCoreopenUtilityincludeTypesmoduleMake=functor(Env:ENV)->functor(E:ENGINEwithtypedict=Env.dict)->structopenEnv(* argument conversion *)(* validate that list has only letters and dots *)letvalidate_letter_dotcs=List.for_allcs~f:(func->(Char.is_lowercasec)||(phys_equalc'.'))(* validate that list has only letters and exactly one dot *)letvalidate_fitcs=validate_letter_dotcs&&(List.countcs~f:(func->phys_equalc'.')=1)letexpand_fitdictcs=letf=function'.'->Dot|c->Letter(from_lowerc)inifvalidate_fitcsthenletpat=List.mapcs~finletchars=E.fitdictpatinGroup(Group.of_char_listchars)elseraise(Invalid_argument"<> group can only have letters and a single .")letexpand_groupdictf=matchfwithFitcs->expand_fitdictcsletexpand_rackdictts=letexpandt=matchtwith|Finalx->x|Expandf->expand_groupdictfinList.mapts~f:expandletsingle_argargs=matchargswith|[arg]->Okarg|_->Error"Expected: Single argument"lettraildictargs=Result.bind(single_argargs)~f:Parser.parse_rack|>Result.ok_or_failwith|>expand_rackdictletlength_patternargs=letn=Result.bind(single_argargs)~f:Parser.parse_int|>Result.ok_or_failwithinList.initn~f:(fun_->Dot)letoverlap_patternposdictarg=letchars=String.to_listarginletpatc=matchposwith`Above->['.';c]|`Below->[c;'.']inletexpc=matchcwith|'.'->Dot|_->expand_fitdict(patc)inletgroups=List.map~f:expcharsingroupsletabove_pattern=overlap_pattern`Aboveletbelow_pattern=overlap_pattern`Below(* prefix functions *)letfn_anagramdictargs=E.anagramdict(traildictargs)~all:false~multi:falseletfn_multidictargs=E.anagramdict(traildictargs)~all:false~multi:trueletfn_builddictargs=E.anagramdict(traildictargs)~all:true~multi:falseletfn_patterndictargs=E.patterndict(traildictargs)letfn_lengthdictargs=E.patterndict(length_patternargs)letfn_abovedictargs=E.patterndict(above_patterndict(List.hd_exnargs))letfn_belowdictargs=E.patterndict(below_patterndict(List.hd_exnargs))letfn_one_offdictargs=E.patterndict(below_patterndict(List.hd_exnargs))(* wordlist generation *)letprefixdictopargs=matchopwith|Anagram->fn_anagramdictargs|Multi->fn_multidictargs|Build->fn_builddictargs|Pattern->fn_patterndictargs|Length->fn_lengthdictargs|Above->fn_abovedictargs|Below->fn_belowdictargs|One_off->fn_one_offdictargs|Fns->Wordset.of_list[s](* binary functions *)letbinaryoplr=let(l,r)=(Wordset.to_lowerl,Wordset.to_lowerr)inmatchopwith|Union->Wordset.unionlr|Inter->Wordset.interlr|Diff->Wordset.difflr|Op_->raiseUnsupported_featureletrecexprenve=matchewith|Wordsw->w|Fun(op,args)->prefixenv.dictopargs|Bop(op,l,r)->binaryop(exprenvl)(exprenvr)|Varv->Vars.getenv.varsvletevalenvline=matchlinewith|Command_->env,Wordset.of_list[]|Expre->env,exprenve|Tilesarg->env,exprenv(Fun(env.op,[arg]))|Assign(v,e)->beginletws=exprenveinletenv'={envwithvars=(Vars.setenv.varsvws)}inenv',wsendend