123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751(************************************************************************)(* * The Coq Proof Assistant / The Coq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)openPpopenUtilopenNamesopenModPathopenNamegenopenNameopsopenTableopenMinimlopenMlutilletascii_of_idid=lets=Id.to_stringidinfori=0toString.lengths-2doifs.[i]=='_'&&s.[i+1]=='_'thenwarning_idsdone;Unicode.ascii_of_identsletis_mp_bound=functionMPbound_->true|_->false(*s Some pretty-print utility functions. *)letpp_parparst=ifparthenstr"("++st++str")"elsest(** [pp_apply] : a head part applied to arguments, possibly with parenthesis *)letpp_applystparargs=matchargswith|[]->st|_->hov2(pp_parpar(st++spc()++prlist_with_sepspcidentityargs))(** Same as [pp_apply], but with also protection of the head by parenthesis *)letpp_apply2stparargs=letpar'=not(List.is_emptyargs)||parinpp_apply(pp_parpar'st)parargsletpr_binding=function|[]->mt()|l->str" "++prlist_with_sep(fun()->str" ")Id.printlletpp_tuple_lightf=function|[]->mt()|[x]->ftruex|l->pp_partrue(prlist_with_sep(fun()->str","++spc())(ffalse)l)letpp_tuplef=function|[]->mt()|[x]->fx|l->pp_partrue(prlist_with_sep(fun()->str","++spc())fl)letpp_boxed_tuplef=function|[]->mt()|[x]->fx|l->pp_partrue(hov0(prlist_with_sep(fun()->str","++spc())fl))letpp_arrayf=function|[]->mt()|[x]->fx|l->pp_partrue(prlist_with_sep(fun()->str";"++spc())fl)(** By default, in module Format, you can do horizontal placing of blocks
even if they include newlines, as long as the number of chars in the
blocks is less that a line length. To avoid this awkward situation,
we attach a big virtual size to [fnl] newlines. *)(* EG: This looks quite suspicious... but beware of bugs *)(* let fnl () = stras (1000000,"") ++ fnl () *)letfnl()=fnl()letfnl2()=fnl()++fnl()letspace_if=functiontrue->str" "|false->mt()letbegins_withsprefix=letlen=String.lengthprefixinString.lengths>=len&&String.equal(String.subs0len)prefixletbegins_with_CoqXXs=letn=String.lengthsinn>=4&&s.[0]=='C'&&s.[1]=='o'&&s.[2]=='q'&&leti=ref3intrywhile!i<ndomatchs.[!i]with|'_'->i:=n(*Stop*)|'0'..'9'->incri|_->raiseNot_founddone;truewithNot_found->falseletunquotes=iflang()!=SchemethenselseString.map(func->ifc=='\''then'~'elsec)sletrecqualifydelim=function|[]->assertfalse|[s]->s|""::l->qualifydeliml|s::l->s^delim^(qualifydeliml)letdottify=qualify"."letpseudo_qualify=qualify"__"(*s Uppercase/lowercase renamings. *)letis_uppers=matchs.[0]with'A'..'Z'->true|_->falseletis_lowers=matchs.[0]with'a'..'z'|'_'->true|_->falseletlowercase_idid=Id.of_string(String.uncapitalize_ascii(ascii_of_idid))letuppercase_idid=lets=ascii_of_ididinassert(not(String.is_emptys));ifs.[0]=='_'thenId.of_string("Coq_"^s)elseId.of_string(String.capitalize_asciis)typekind=Term|Type|Cons|ModmoduleKOrd=structtypet=kind*stringletcompare(k1,s1)(k2,s2)=letc=Stdlib.comparek1k2(* OK *)inifc=0thenString.compares1s2elsecendmoduleKMap=Map.Make(KOrd)letupperkind=function|Type->lang()==Haskell|Term->false|Cons|Mod->trueletkindcase_idkid=ifupperkindkthenuppercase_ididelselowercase_idid(*s de Bruijn environments for programs *)typeenv=Id.tlist*Id.Set.t(*s Generic renaming issues for local variable names. *)letrecrename_ididavoid=ifId.Set.memidavoidthenrename_id(increment_subscriptid)avoidelseidletrecrename_varsavoid=function|[]->[],avoid|id::idlwhenid==dummy_name->(* we don't rename dummy binders *)let(idl',avoid')=rename_varsavoididlin(id::idl',avoid')|id::idl->let(idl,avoid)=rename_varsavoididlinletid=rename_id(lowercase_idid)avoidin(id::idl,Id.Set.addidavoid)letrename_tvarsavoidl=letrecrenameavoid=function|[]->[],avoid|id::idl->letid=rename_id(lowercase_idid)avoidinletidl,avoid=rename(Id.Set.addidavoid)idlin(id::idl,avoid)infst(renameavoidl)letpush_varsids(db,avoid)=letids',avoid'=rename_varsavoididsinids',(ids'@db,avoid')letget_db_namen(db,_)=List.nthdb(predn)(*S Renamings of global objects. *)(*s Tables of global renamings *)letregister_cleanup,do_cleanup=letfuns=ref[]in(funf->funs:=f::!funs),(fun()->List.iter(funf->f())!funs)typephase=Pre|Impl|Intfletset_phase,get_phase=letph=refImplin((:=)ph),(fun()->!ph)letset_keywords,get_keywords=letk=refId.Set.emptyin((:=)k),(fun()->!k)letadd_global_ids,get_global_ids=letids=refId.Set.emptyinregister_cleanup(fun()->ids:=get_keywords());letadds=ids:=Id.Set.adds!idsandget()=!idsin(add,get)letempty_env()=[],get_global_ids()(* We might have built [global_reference] whose canonical part is
inaccurate. We must hence compare only the user part,
hence using a Hashtbl might be incorrect *)letmktable_idautoclean=letm=refId.Map.emptyinletclear()=m:=Id.Map.emptyinifautocleanthenregister_cleanupclear;(funrv->m:=Id.Map.addrv!m),(funr->Id.Map.findr!m),clearletmktable_refautoclean=letm=refRefmap'.emptyinletclear()=m:=Refmap'.emptyinifautocleanthenregister_cleanupclear;(funrv->m:=Refmap'.addrv!m),(funr->Refmap'.findr!m),clearletmktable_modpathautoclean=letm=refMPmap.emptyinletclear()=m:=MPmap.emptyinifautocleanthenregister_cleanupclear;(funrv->m:=MPmap.addrv!m),(funr->MPmap.findr!m),clear(* A table recording objects in the first level of all MPfile *)letadd_mpfiles_content,get_mpfiles_content,clear_mpfiles_content=mktable_modpathfalseletget_mpfiles_contentmp=tryget_mpfiles_contentmpwithNot_found->failwith"get_mpfiles_content"(*s The list of external modules that will be opened initially *)letmpfiles_add,mpfiles_mem,mpfiles_list,mpfiles_clear=letm=refMPset.emptyinletaddmp=m:=MPset.addmp!mandmemmp=MPset.memmp!mandlist()=MPset.elements!mandclear()=m:=MPset.emptyinregister_cleanupclear;(add,mem,list,clear)(*s List of module parameters that we should alpha-rename *)letparams_ren_add,params_ren_mem=letm=refMPset.emptyinletaddmp=m:=MPset.addmp!mandmemmp=MPset.memmp!mandclear()=m:=MPset.emptyinregister_cleanupclear;(add,mem)(*s table indicating the visible horizon at a precise moment,
i.e. the stack of structures we are inside.
- The sequence of [mp] parts should have the following form:
a [MPfile] at the beginning, and then more and more [MPdot]
over this [MPfile], or [MPbound] when inside the type of a
module parameter.
- the [params] are the [MPbound] when [mp] is a functor,
the innermost [MPbound] coming first in the list.
- The [content] part is used to record all the names already
seen at this level.
*)typevisible_layer={mp:ModPath.t;params:ModPath.tlist;mutablecontent:Label.tKMap.t;}letpop_visible,push_visible,get_visible=letvis=ref[]inregister_cleanup(fun()->vis:=[]);letpop()=match!viswith|[]->assertfalse|v::vl->vis:=vl;(* we save the 1st-level-content of MPfile for later use *)ifget_phase()==Impl&&modular()&&is_modfilev.mpthenadd_mpfiles_contentv.mpv.contentandpushmpmps=vis:={mp=mp;params=mps;content=KMap.empty}::!visandget()=!visin(pop,push,get)letget_visible_mps()=List.map(functionv->v.mp)(get_visible())lettop_visible()=matchget_visible()with[]->assertfalse|v::_->vlettop_visible_mp()=(top_visible()).mpletadd_visibleksl=letvisible=top_visible()invisible.content<-KMap.addkslvisible.content(* table of local module wrappers used to provide non-ambiguous names *)moduleDupOrd=structtypet=ModPath.t*Label.tletcompare(mp1,l1)(mp2,l2)=letc=Label.comparel1l2inifInt.equalc0thenModPath.comparemp1mp2elsecendmoduleDupMap=Map.Make(DupOrd)letadd_duplicate,get_duplicate=letindex=ref0anddups=refDupMap.emptyinregister_cleanup(fun()->index:=0;dups:=DupMap.empty);letaddmpl=incrindex;letren="Coq__"^string_of_int!indexindups:=DupMap.add(mp,l)ren!dupsandgetmpl=trySome(DupMap.find(mp,l)!dups)withNot_found->Nonein(add,get)typereset_kind=AllButExternal|Everythingletreset_renaming_tablesflag=do_cleanup();ifflag==Everythingthenclear_mpfiles_content()(*S Renaming functions *)(* This function creates from [id] a correct uppercase/lowercase identifier.
This is done by adding a [Coq_] or [coq_] prefix. To avoid potential clashes
with previous [Coq_id] variable, these prefixes are duplicated if already
existing. *)letmodular_renamekid=lets=ascii_of_ididinletprefix,is_ok=ifupperkindkthen"Coq_",is_upperelse"coq_",is_lowerinifnot(is_oks)||Id.Set.memid(get_keywords())||begins_withsprefixthenprefix^selses(*s For monolithic extraction, first-level modules might have to be renamed
with unique numbers *)letmodfstlev_rename=letadd_index,get_index,_=mktable_idtrueinfunl->letid=Label.to_idlintryletn=get_indexidinadd_indexid(n+1);lets=ifn==0then""elsestring_of_int(n-1)in"Coq"^s^"_"^(ascii_of_idid)withNot_found->lets=ascii_of_ididinifis_lowers||begins_with_CoqXXsthen(add_indexid1;"Coq_"^s)else(add_indexid0;s)(*s Creating renaming for a [module_path] : first, the real function ... *)letrecmp_renaming_funmp=matchmpwith|_whennot(modular())&&at_toplevelmp->[""]|MPdot(mp,l)->letlmp=mp_renamingmpinletmp=matchlmpwith|[""]->modfstlev_renamel|_->modular_renameMod(Label.to_idl)inmp::lmp|MPboundmbid->lets=modular_renameMod(MBId.to_idmbid)inifnot(params_ren_memmp)then[s]elseleti,_,_=MBId.reprmbidin[s^"__"^string_of_inti]|MPfile_->assert(modular());(* see [at_toplevel] above *)assert(get_phase()==Pre);letcurrent_mpfile=(List.last(get_visible())).mpinifnot(ModPath.equalmpcurrent_mpfile)thenmpfiles_addmp;[string_of_modfilemp](* ... and its version using a cache *)andmp_renaming=letadd,get,_=mktable_modpathtrueinfunx->tryifis_mp_bound(base_mpx)thenraiseNot_found;getxwithNot_found->lety=mp_renaming_funxinaddxy;y(*s Renamings creation for a [global_reference]: we build its fully-qualified
name in a [string list] form (head is the short name). *)letref_renaming_fun(k,r)=letmp=modpath_of_rrinletl=mp_renamingmpinletl=iflang()!=Ocaml&¬(modular())then[""]elselinlets=letidg=safe_basename_of_globalrinmatchlwith|[""]->(* this happens only at toplevel of the monolithic case *)letglobs=get_global_ids()inletid=next_ident_away(kindcase_idkidg)globsinId.to_stringid|_->modular_renamekidginadd_global_ids(Id.of_strings);s::l(* Cached version of the last function *)letref_renaming=letadd,get,_=mktable_reftrueinfun((k,r)asx)->tryifis_mp_bound(base_mp(modpath_of_rr))thenraiseNot_found;getrwithNot_found->lety=ref_renaming_funxinaddry;y(* [visible_clash mp0 (k,s)] checks if [mp0-s] of kind [k]
can be printed as [s] in the current context of visible
modules. More precisely, we check if there exists a
visible [mp] that contains [s].
The verification stops if we encounter [mp=mp0]. *)letrecclashmemmp0ks=function|[]->false|mp::_whenModPath.equalmpmp0->false|mp::_whenmemmpks->true|_::mpl->clashmemmp0ksmplletmpfiles_clashmp0ks=clash(funmpk->KMap.memk(get_mpfiles_contentmp))mp0ks(List.rev(mpfiles_list()))letrecparams_lookupmp0ks=function|[]->false|param::_whenModPath.equalmp0param->true|param::params->let()=matchkswith|(Mod,mp)whenString.equal(List.hd(mp_renamingparam))mp->params_ren_addparam|_->()inparams_lookupmp0ksparamsletvisible_clashmp0ks=letrecclash=function|[]->false|v::_whenModPath.equalv.mpmp0->false|v::vis->letb=KMap.memksv.contentinifb&¬(is_mp_boundmp0)thentrueelsebeginifbthenparams_ren_addmp0;ifparams_lookupmp0ksv.paramsthenfalseelseclashvisendinclash(get_visible())(* Same, but with verbose output (and mp0 shouldn't be a MPbound) *)letvisible_clash_dbgmp0ks=letrecclash=function|[]->None|v::_whenModPath.equalv.mpmp0->None|v::vis->trySome(v.mp,KMap.findksv.content)withNot_found->ifparams_lookupmp0ksv.paramsthenNoneelseclashvisinclash(get_visible())(* After the 1st pass, we can decide which modules will be opened initially *)letopened_libraries()=ifnot(modular())then[]elseletused_files=mpfiles_list()inletused_ks=List.map(funmp->Mod,string_of_modfilemp)used_filesin(* By default, we open all used files. Ambiguities will be resolved later
by using qualified names. Nonetheless, we don't open any file A that
contains an immediate submodule A.B hiding another file B : otherwise,
after such an open, there's no unambiguous way to refer to objects of B. *)letto_open=List.filter(funmp->not(List.exists(funk->KMap.memk(get_mpfiles_contentmp))used_ks))used_filesinmpfiles_clear();List.itermpfiles_addto_open;mpfiles_list()(*s On-the-fly qualification issues for both monolithic or modular extraction. *)(* [pp_ocaml_gen] below is a function that factorize the printing of both
[global_reference] and module names for ocaml. When [k=Mod] then [olab=None],
otherwise it contains the label of the reference to print.
[rls] is the string list giving the qualified name, short name at the end. *)(* In Coq, we can qualify [M.t] even if we are inside [M], but in Ocaml we
cannot do that. So, if [t] gets hidden and we need a long name for it,
we duplicate the _definition_ of t in a Coq__XXX module, and similarly
for a sub-module [M.N] *)letpp_duplicatek'prefixmprlsolab=letrls',lbl=ifk'!=Modthen(* Here rls=[s], the ref to print is <prefix>.<s>, and olab<>None *)rls,Option.getolabelse(* Here rls=s::rls', we search the label for s inside mp *)List.tlrls,get_nth_label_mp(mp_lengthmp-mp_lengthprefix)mpinmatchget_duplicateprefixlblwith|Someren->dottify(ren::rls')|None->assert(get_phase()==Pre);(* otherwise it's too late *)add_duplicateprefixlbl;dottifyrlsletfstlev_ksk=function|[]->assertfalse|[s]->k,s|s::_->Mod,s(* [pp_ocaml_local] : [mp] has something in common with [top_visible ()]
but isn't equal to it *)letpp_ocaml_localkprefixmprlsolab=(* what is the largest prefix of [mp] that belongs to [visible]? *)assert(k!=Mod||not(ModPath.equalmpprefix));(* mp as whole module isn't in itself *)letrls'=List.skipn(mp_lengthprefix)rlsinletk's=fstlev_kskrls'in(* Reference r / module path mp is of the form [<prefix>.s.<...>]. *)ifnot(visible_clashprefixk's)thendottifyrls'elsepp_duplicate(fstk's)prefixmprls'olab(* [pp_ocaml_bound] : [mp] starts with a [MPbound], and we are not inside
(i.e. we are not printing the type of the module parameter) *)letpp_ocaml_boundbaserls=(* clash with a MPbound will be detected and fixed by renaming this MPbound *)ifget_phase()==Prethenignore(visible_clashbase(Mod,List.hdrls));dottifyrls(* [pp_ocaml_extern] : [mp] isn't local, it is defined in another [MPfile]. *)letpp_ocaml_externkbaserls=matchrlswith|[]->assertfalse|base_s::rls'->if(not(modular()))(* Pseudo qualification with "" *)||(List.is_emptyrls')(* Case of a file A.v used as a module later *)||(not(mpfiles_membase))(* Module not opened *)||(mpfiles_clashbase(fstlev_kskrls'))(* Conflict in opened files *)||(visible_clashbase(fstlev_kskrls'))(* Local conflict *)then(* We need to fully qualify. Last clash situation is unsupported *)matchvisible_clash_dbgbase(Mod,base_s)with|None->dottifyrls|Some(mp,l)->error_module_clashbase(MPdot(mp,l))else(* Standard situation : object in an opened file *)dottifyrls'(* [pp_ocaml_gen] : choosing between [pp_ocaml_local] or [pp_ocaml_extern] *)letpp_ocaml_genkmprlsolab=matchcommon_prefix_from_listmp(get_visible_mps())with|Someprefix->pp_ocaml_localkprefixmprlsolab|None->letbase=base_mpmpinifis_mp_boundbasethenpp_ocaml_boundbaserlselsepp_ocaml_externkbaserls(* For Haskell, things are simpler: we have removed (almost) all structures *)letpp_haskell_genkmprls=matchrlswith|[]->assertfalse|s::rls'->letstr=pseudo_qualifyrls'inletstr=ifis_upperstr&¬(upperkindk)then("_"^str)elsestrinifModPath.equal(base_mpmp)(top_visible_mp())thenstrelses^"."^str(* Main name printing function for a reference *)letpp_global_with_keykkeyr=letls=ref_renaming(k,r)inassert(List.lengthls>1);lets=List.hdlsinletmp,l=KerName.reprkeyinifModPath.equalmp(top_visible_mp())then(* simplest situation: definition of r (or use in the same context) *)(* we update the visible environment *)(add_visible(k,s)l;unquotes)elseletrls=List.revlsin(* for what come next it's easier this way *)matchlang()with|Scheme->unquotes(* no modular Scheme extraction... *)|JSON->dottify(List.mapunquoterls)|Haskell->ifmodular()thenpp_haskell_genkmprlselses|Ocaml->pp_ocaml_genkmprls(Somel)letpp_globalkr=pp_global_with_keyk(repr_of_rr)r(* Main name printing function for declaring a reference *)letpp_global_namekr=letls=ref_renaming(k,r)inassert(List.lengthls>1);List.hdls(* The next function is used only in Ocaml extraction...*)letpp_modulemp=letls=mp_renamingmpinmatchmpwith|MPdot(mp0,l)whenModPath.equalmp0(top_visible_mp())->(* simplest situation: definition of mp (or use in the same context) *)(* we update the visible environment *)lets=List.hdlsinadd_visible(Mod,s)l;s|_->pp_ocaml_genModmp(List.revls)None(** Special hack for constants of type Ascii.ascii : if an
[Extract Inductive ascii => char] has been declared, then
the constants are directly turned into chars *)letascii_type_name="core.ascii.type"letascii_constructor_name="core.ascii.ascii"letis_ascii_registered()=Coqlib.has_refascii_type_name&&Coqlib.has_refascii_constructor_nameletascii_type_ref()=Coqlib.lib_refascii_type_nameletcheck_extract_ascii()=tryletchar_type=matchlang()with|Ocaml->"char"|Haskell->"Prelude.Char"|_->raiseNot_foundinString.equal(find_custom@@ascii_type_ref())(char_type)withNot_found->falseletis_list_consl=List.for_all(functionMLcons(_,GlobRef.ConstructRef(_,_),[])->true|_->false)lletis_native_char=function|MLcons(_,gr,l)->is_ascii_registered()&&Coqlib.check_refascii_constructor_namegr&&check_extract_ascii()&&is_list_consl|_->falseletget_native_charc=letreccumul=function|[]->0|MLcons(_,GlobRef.ConstructRef(_,j),[])::l->(2-j)+2*(cumull)|_->assertfalseinletl=matchcwithMLcons(_,_,l)->l|_->assertfalseinChar.chr(cumull)letpp_native_charc=str("'"^Char.escaped(get_native_charc)^"'")(** Special hack for constants of type String.string : if an
[Extract Inductive string => string] has been declared, then
the constants are directly turned into string literals *)letstring_type_name="core.string.type"letempty_string_name="core.string.empty"letstring_constructor_name="core.string.string"letis_string_registered()=Coqlib.has_refstring_type_name&&Coqlib.has_refempty_string_name&&Coqlib.has_refstring_constructor_nameletstring_type_ref()=Coqlib.lib_refstring_type_nameletcheck_extract_string()=tryletstring_type=matchlang()with|Ocaml->"string"|Haskell->"Prelude.String"|_->raiseNot_foundinString.equal(find_custom@@string_type_ref())string_typewithNot_found->false(* The argument is known to be of type Coq.Strings.String.string.
Check that it is built from constructors EmptyString and String
with constant ascii arguments. *)letrecis_native_string_recempty_string_refstring_constructor_ref=function(* "EmptyString" constructor *)|MLcons(_,gr,[])->Coqlib.check_refempty_string_refgr(* "String" constructor *)|MLcons(_,gr,[hd;tl])->Coqlib.check_refstring_constructor_refgr&&is_native_charhd&&is_native_string_recempty_string_refstring_constructor_reftl(* others *)|_->false(* Here we first check that the argument is the type registered as
core.string.type and that extraction to native strings was
requested. Then we check every character via
[is_native_string_rec]. *)letis_native_stringc=matchcwith|MLcons(_,GlobRef.ConstructRef(ind,j),l)->is_string_registered()&&Coqlib.check_refstring_type_name(GlobRef.IndRefind)&&check_extract_string()&&is_native_string_recempty_string_namestring_constructor_namec|_->false(* Extract the underlying string. *)letget_native_stringc=letbuf=Buffer.create64inletrecget=function(* "EmptyString" constructor *)|MLcons(_,gr,[])whenCoqlib.check_refempty_string_namegr->Buffer.contentsbuf(* "String" constructor *)|MLcons(_,gr,[hd;tl])whenCoqlib.check_refstring_constructor_namegr->Buffer.add_charbuf(get_native_charhd);gettl(* others *)|_->assertfalseingetc(* Printing the underlying string. *)letpp_native_stringc=str("\""^String.escaped(get_native_stringc)^"\"")(* Registered sig type *)letsig_type_name="core.sig.type"