123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124openModule_typesopenCommonmoduletypeS=sigtypea(* The result of the argument parser *)typekey=string(* option key, must start with '-' *)typedoc=string(* option description *)typespec(* specification of an option *)=|Unitof(a->a)(* option with no argument *)|Stringof(string->a->a)(* option with string argument *)|Intof(int->a->a)(* option with integer argument *)typeanon=string->a->a(* function taking an anonymus argument into
the result *)typeerror=|Unknown_optionofstring|Missing_argumentofkey*spec*doc|Invalid_argumentofkey*spec*doc*stringvalparse:stringarray->a->(key*spec*doc)list->anon->(a,error)resultvalstring_of_error:error->stringvalargument_type:spec->stringendmoduleMake(A:ANY)=structtypea=A.ttypekey=stringtypedoc=stringtypespec=|Unitof(a->a)(* option with no argument *)|Stringof(string->a->a)(* option with string argument *)|Intof(int->a->a)(* option with integer argument *)typeerror=|Unknown_optionofstring|Missing_argumentofkey*spec*doc|Invalid_argumentofkey*spec*doc*stringmoduleM=Monad.Result(structtypet=errorend)typeanon=string->a->aletargument_type(spec:spec):string=matchspecwith|Unit_->""|String_->" <string>"|Int_->" <int>"letparse(args:stringarray)(start:a)(options:(key*spec*doc)list)(anon:anon):(a,error)result=letlen=Array.lengthargsinletrecparse(a:a)(i:int):aM.t=ifi=lenthenM.returnaelseletarg=args.(i)inletn=String.lengtharginifn=0thenparsea(i+1)elseifarg.[0]='-'thenmatchList.find(fun(k,_,_)->k=arg)optionswith|None->M.throw(Unknown_optionarg)|Some(k,sp,doc)->beginmatchspwith|Unitg->parse(ga)(i+1)|Stringg->ifi+1=lenthenM.throw(Missing_argument(k,sp,doc))elseparse(gargs.(i+1)a)(i+2)|Intg->ifi+1=lenthenM.throw(Missing_argument(k,sp,doc))elsetryparse(g(int_of_stringargs.(i+1))a)(i+2)withFailure_->M.throw(Invalid_argument(k,sp,doc,args.(i+1)))endelseparse(anonarga)(i+1)inparsestart1letstring_of_error(e:error):string=letoption_expectks="option '"^k^"' expects a "^(matchswith|Unit_->assertfalse|String_->"string"|Int_->"integer")inmatchewith|Unknown_optionstr->"unknown option '"^str^"'"|Missing_argument(k,s,_)->"missing argument; "^option_expectks|Invalid_argument(k,s,_,arg)->"invalid argument '"^arg^"'; "^option_expectksend