123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165moduletypeIntSig=sigtypetvalname:stringvalfmt:stringvalzero:tvalmax_int:tvalmin_int:tvalbits:intvalof_int:int->tvalto_int:t->intvaladd:t->t->tvalsub:t->t->tvalmul:t->t->tvaldivmod:t->t->t*tendmoduletypeS=sigtypetvalof_substring:string->pos:int->(t*int)valof_string:string->tvalto_string:t->stringvalto_string_bin:t->stringvalto_string_oct:t->stringvalto_string_hex:t->stringvalprinter:Format.formatter->t->unitvalprinter_bin:Format.formatter->t->unitvalprinter_oct:Format.formatter->t->unitvalprinter_hex:Format.formatter->t->unitendmoduleMake(I:IntSig):Swithtypet=I.t=structtypet=I.texceptionEndOfNumberofI.t*int(** Base function for *of_string* and *of_substring*
* functions *)let_of_substringstart_offsfunc_name=letfail()=invalid_arg(I.name^func_name)inifstart_off>=String.lengthsthenfail();(* is this supposed to be a negative number? *)letnegative,off=ifs.[start_off]='-'thentrue,1+start_offelseifs.[start_off]='+'thenfalse,1+start_offelsefalse,start_offinletlen=String.lengthsiniflen<=offthenfail();(* does the string have a base-prefix and what base is it? *)letbase,off=letis_digit~basec=ifbase<=10then(Char.(codec-code'0')<base)else((c>='0'&&c<='9')||(10+Char.(code(lowercase_asciic)-code'a')<base))iniflen-off<3then(* no space for a prefix in there *)10,offelseifs.[off]='0'thenmatchChar.lowercase_asciis.[off+1]with|'b'whenis_digit~base:2s.[off+2]->2,off+2|'o'whenis_digit~base:8s.[off+2]->8,off+2|'x'whenis_digit~base:16s.[off+2]->16,off+2|_->10,offelse10,offinletbase=I.of_intbasein(* operators that are different for parsing negative and positive numbers *)let(thresh,rem),join,cmp_safe=ifnegativethen(I.divmodI.min_intbase,I.sub,1)else(I.divmodI.max_intbase,I.add,-1)inletrecloopoff(n:I.t)=ifoff=lenthenn,offelsebeginletc=s.[off]inifc<>'_'thenbeginletdisp=ifc>='0'&&c<='9'then48elseifc>='A'&&c<='F'then55elseifc>='a'&&c<='f'then87elseraise(EndOfNumber(n,off))inletdisp=int_of_charc-dispinletd=I.of_intdispin(* do not accept digit larger than the base *)ifd>=basethenraise(EndOfNumber(n,off));(* will we overflow? *)(matchcomparenthreshwith|0->letr=comparedreminifr<>cmp_safe&&r<>0thenraise(EndOfNumber(n,off));|r->ifr<>cmp_safethenraise(EndOfNumber(n,off)));(* shift the existing number, join the new digit *)letres=join(I.mulnbase)dinloop(off+1)resendelseloop(off+1)nendinloopoffI.zeroletof_substrings~pos=try_of_substringposs".of_substring"with|EndOfNumber(n,off)->n,offletof_strings=tryletn,_=_of_substring0s".of_string"innwith|EndOfNumber_->invalid_arg(I.name^".of_string")letto_string_basebaseprefixx=letprefixlen=String.lengthprefixinletbase=I.of_intbaseinletconv="0123456789abcdef"inifx=I.zerothenprefix^"0"elsebegin(* worst-case: 1 (signed) + length prefix + 1 char-per-bit *)letmaxlen=1+prefixlen+I.bitsinletbuffer=Bytes.createmaxlenin(* create the number starting at the end of the buffer, working towards
* its start. *)letoff=ref(maxlen-1)inletrecloopn=ifn<>I.zerothenbeginletn',digit=I.divmodnbaseinletdigit=(I.to_intdigit)inBytes.setbuffer!offconv.[absdigit];decroff;loopn'endinloopx;(* add prefix -- in reverse order *)fori=prefixlen-1downto0doBytes.setbuffer!off(String.getprefixi);decroffdone;ifx<I.zerothenbeginBytes.setbuffer!off'-';decroffend;Bytes.sub_stringbuffer(!off+1)(maxlen-!off-1)endletto_string=to_string_base10""letto_string_bin=to_string_base2"0b"letto_string_oct=to_string_base8"0o"letto_string_hex=to_string_base16"0x"letprint_withffmtx=Format.fprintffmt"@[%s@]"(fx^I.fmt)letprinter=print_withto_stringletprinter_bin=print_withto_string_binletprinter_oct=print_withto_string_octletprinter_hex=print_withto_string_hexend