123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399(***********************************************************************)(* omd: Markdown frontend in OCaml *)(* (c) 2013 by Philippe Wang <philippe.wang@cl.cam.ac.uk> *)(* Licence : ISC *)(* http://www.isc.org/downloads/software-support-policy/isc-license/ *)(***********************************************************************)(* Implementation notes *********************************************
* - This module should depend on OCaml's standard library only and
* should be as 'pure OCaml' (i.e. depend as least as possible on
* external tools) as possible.
* - `while' loops are sometimes preferred to recursion because this
* may be used on systems where tail recursion is not well
* supported. (I tried to write "while" as often as possible, but it
* turned out that it was pretty inconvenient, so I do use
* recursion. When I have time, I'll do some tests and see if I
* need to convert recursive loops into iterative loops. Sorry if it
* makes it harder to read.)
*)(* class type tag = object method is_me : 'a. 'a -> bool end *)openOmd_representationtypetoken=Omd_representation.toktypet=Omd_representation.toklistletstring_of_token=function|Tag(name,o)->ifOmd_utils.debugthen"TAG("^name^")"^o#to_stringelseo#to_string|Ampersand->"&"|Ampersandsn->assert(n>=0);String.make(2+n)'&'|At->"@"|Atsn->assert(n>=0);String.make(2+n)'@'|Backquote->"`"|Backquotesn->assert(n>=0);String.make(2+n)'`'|Backslash->"\\"|Backslashsn->assert(n>=0);String.make(2+n)'\\'|Bar->"|"|Barsn->assert(n>=0);String.make(2+n)'|'|Caret->"^"|Caretsn->assert(n>=0);String.make(2+n)'^'|Cbrace->"}"|Cbracesn->assert(n>=0);String.make(2+n)'}'|Colon->":"|Colonsn->assert(n>=0);String.make(2+n)':'|Comma->","|Commasn->assert(n>=0);String.make(2+n)','|Cparenthesis->")"|Cparenthesissn->assert(n>=0);String.make(2+n)')'|Cbracket->"]"|Cbracketsn->assert(n>=0);String.make(2+n)']'|Dollar->"$"|Dollarsn->assert(n>=0);String.make(2+n)'$'|Dot->"."|Dotsn->assert(n>=0);String.make(2+n)'.'|Doublequote->"\""|Doublequotesn->assert(n>=0);String.make(2+n)'"'|Exclamation->"!"|Exclamationsn->assert(n>=0);String.make(2+n)'!'|Equal->"="|Equalsn->assert(n>=0);String.make(2+n)'='|Greaterthan->">"|Greaterthansn->assert(n>=0);String.make(2+n)'>'|Hash->"#"|Hashsn->assert(n>=0);String.make(2+n)'#'|Lessthan->"<"|Lessthansn->assert(n>=0);String.make(2+n)'<'|Minus->"-"|Minussn->assert(n>=0);String.make(2+n)'-'|Newline->"\n"|Newlinesn->assert(n>=0);String.make(2+n)'\n'|Numbers->s|Obrace->"{"|Obracesn->assert(n>=0);String.make(2+n)'{'|Oparenthesis->"("|Oparenthesissn->assert(n>=0);String.make(2+n)'('|Obracket->"["|Obracketsn->assert(n>=0);String.make(2+n)'['|Percent->"%"|Percentsn->assert(n>=0);String.make(2+n)'%'|Plus->"+"|Plussn->assert(n>=0);String.make(2+n)'+'|Question->"?"|Questionsn->assert(n>=0);String.make(2+n)'?'|Quote->"'"|Quotesn->assert(n>=0);String.make(2+n)'\''|Semicolon->";"|Semicolonsn->assert(n>=0);String.make(2+n)';'|Slash->"/"|Slashsn->assert(n>=0);String.make(2+n)'/'|Space->" "|Spacesn->assert(n>=0);String.make(2+n)' '|Star->"*"|Starsn->assert(n>=0);String.make(2+n)'*'|Tab->" "|Tabsn->assert(n>=0);String.make((2+n)*4)' '|Tilde->"~"|Tildesn->assert(n>=0);String.make(2+n)'~'|Underscore->"_"|Underscoresn->assert(n>=0);String.make(2+n)'_'|Words->sletsize_and_newlines=function|Tag_->(0,0)|Ampersand|At|Backquote|Backslash|Bar|Caret|Cbrace|Colon|Comma|Cparenthesis|Cbracket|Dollar|Dot|Doublequote|Exclamation|Equal|Greaterthan|Hash|Lessthan|Minus|Obrace|Oparenthesis|Obracket|Percent|Plus|Question|Quote|Semicolon|Slash|Space|Star|Tab|Tilde|Underscore->(1,0)|Ampersandsx|Atsx|Backquotesx|Backslashsx|Barsx|Caretsx|Cbracesx|Colonsx|Commasx|Cparenthesissx|Cbracketsx|Dollarsx|Dotsx|Doublequotesx|Exclamationsx|Equalsx|Greaterthansx|Hashsx|Lessthansx|Minussx|Obracesx|Oparenthesissx|Obracketsx|Percentsx|Plussx|Questionsx|Quotesx|Semicolonsx|Slashsx|Spacesx|Starsx|Tabsx|Tildesx|Underscoresx->(2+x,0)|Newline->(0,1)|Newlinesx->(0,2+x)|Numbers|Words->(String.lengths,0)letlengtht=letc,nl=size_and_newlinestinc+nlletsplit_first=function|Ampersandsn->Ampersand,(ifn>0thenAmpersands(n-1)elseAmpersand)|Atsn->At,(ifn>0thenAts(n-1)elseAt)|Backquotesn->Backquote,(ifn>0thenBackquotes(n-1)elseBackquote)|Backslashsn->Backslash,(ifn>0thenBackslashs(n-1)elseBackslash)|Barsn->Bar,(ifn>0thenBars(n-1)elseBar)|Caretsn->Caret,(ifn>0thenCarets(n-1)elseCaret)|Cbracesn->Cbrace,(ifn>0thenCbraces(n-1)elseCbrace)|Colonsn->Colon,(ifn>0thenColons(n-1)elseColon)|Commasn->Comma,(ifn>0thenCommas(n-1)elseComma)|Cparenthesissn->Cparenthesis,(ifn>0thenCparenthesiss(n-1)elseCparenthesis)|Cbracketsn->Cbracket,(ifn>0thenCbrackets(n-1)elseCbracket)|Dollarsn->Dollar,(ifn>0thenDollars(n-1)elseDollar)|Dotsn->Dot,(ifn>0thenDots(n-1)elseDot)|Doublequotesn->Doublequote,(ifn>0thenDoublequotes(n-1)elseDoublequote)|Exclamationsn->Exclamation,(ifn>0thenExclamations(n-1)elseExclamation)|Equalsn->Equal,(ifn>0thenEquals(n-1)elseEqual)|Greaterthansn->Greaterthan,(ifn>0thenGreaterthans(n-1)elseGreaterthan)|Hashsn->Hash,(ifn>0thenHashs(n-1)elseHash)|Lessthansn->Lessthan,(ifn>0thenLessthans(n-1)elseLessthan)|Minussn->Minus,(ifn>0thenMinuss(n-1)elseMinus)|Newlinesn->Newline,(ifn>0thenNewlines(n-1)elseNewline)|Obracesn->Obrace,(ifn>0thenObraces(n-1)elseObrace)|Oparenthesissn->Oparenthesis,(ifn>0thenOparenthesiss(n-1)elseOparenthesis)|Obracketsn->Obracket,(ifn>0thenObrackets(n-1)elseObracket)|Percentsn->Percent,(ifn>0thenPercents(n-1)elsePercent)|Plussn->Plus,(ifn>0thenPluss(n-1)elsePlus)|Questionsn->Question,(ifn>0thenQuestions(n-1)elseQuestion)|Quotesn->Quote,(ifn>0thenQuotes(n-1)elseQuote)|Semicolonsn->Semicolon,(ifn>0thenSemicolons(n-1)elseSemicolon)|Slashsn->Slash,(ifn>0thenSlashs(n-1)elseSlash)|Spacesn->Space,(ifn>0thenSpaces(n-1)elseSpace)|Starsn->Star,(ifn>0thenStars(n-1)elseStar)|Tabsn->Tab,(ifn>0thenTabs(n-1)elseTab)|Tildesn->Tilde,(ifn>0thenTildes(n-1)elseTilde)|Underscoresn->Underscore,(ifn>0thenUnderscores(n-1)elseUnderscore)|Ampersand|At|Backquote|Backslash|Bar|Caret|Cbrace|Colon|Comma|Cparenthesis|Cbracket|Dollar|Dot|Doublequote|Exclamation|Equal|Greaterthan|Hash|Lessthan|Minus|Newline|Number_|Obrace|Oparenthesis|Obracket|Percent|Plus|Question|Quote|Semicolon|Slash|Space|Star|Tab|Tilde|Underscore|Tag_|Word_->invalid_arg"Omd_lexer.split_first"moduletypeInput=sigtypetvallength:t->intvalget:t->int->charvalsub:t->pos:int->len:int->stringendmoduleLex(I:Input):sigvallex:I.t->tend=structletlex(s:I.t)=letresult=ref[]inleti=ref0inletl=I.lengthsinletrcountc=(* [rcount c] returns the number of immediate consecutive
occurrences of [c]. By side-effect, it increases the reference
counter [i]. *)letrecloopr=if!i=lthenrelseifI.gets!i=cthen(incri;loop(r+1))elserinloop1inletword()=letstart=!iinletrecloop()=beginif!i=lthenWord(I.subs~pos:start~len:(!i-start))elsematchI.gets!iwith|' '|'\t'|'\n'|'\r'|'#'|'*'|'-'|'+'|'`'|'\''|'"'|'\\'|'_'|'['|']'|'{'|'}'|'('|')'|':'|';'|'>'|'~'|'<'|'@'|'&'|'|'|'^'|'.'|'/'|'$'|'%'|'!'|'?'|'='->Word(I.subs~pos:start~len:(!i-start))|c->incri;loop()endinloop()inletmaybe_number()=letstart=!iinwhile!i<l&&matchI.gets!iwith|'0'..'9'->true|_->falsedoincridone;if!i=lthenNumber(I.subs~pos:start~len:(!i-start))elsebeginmatchI.gets!iwith|' '|'\t'|'\n'|'\r'|'#'|'*'|'-'|'+'|'`'|'\''|'"'|'\\'|'_'|'['|']'|'{'|'}'|'('|')'|':'|';'|'>'|'~'|'<'|'@'|'&'|'|'|'^'|'.'|'/'|'$'|'%'|'!'|'?'|'='->Number(I.subs~pos:start~len:(!i-start))|_->i:=start;word()endinletn_occc=incri;rcountcinwhile!i<ldoletc=I.gets!iinletw=matchcwith|' '->letn=n_occcinifn=1thenSpaceelseSpaces(n-2)|'\t'->letn=n_occcinifn=1thenSpaces(2)elseSpaces(4*n-2)|'\n'->letn=n_occcinifn=1thenNewlineelseNewlines(n-2)|'\r'->(* eliminating \r by converting all styles to unix style *)incri;letreccount_rnx=if!i<l&&I.gets(!i)='\n'thenif!i+1<l&&I.gets(!i+1)='\r'then(i:=!i+2;count_rn(x+1))elsexelsexinletrn=1+count_rn0inifrn=1thenmatchn_occcwith|1->Newline|x->assert(x>=2);Newlines(x-2)else(assert(rn>=2);Newlines(rn-2))|'#'->letn=n_occcinifn=1thenHashelseHashs(n-2)|'*'->letn=n_occcinifn=1thenStarelseStars(n-2)|'-'->letn=n_occcinifn=1thenMinuselseMinuss(n-2)|'+'->letn=n_occcinifn=1thenPluselsePluss(n-2)|'`'->letn=n_occcinifn=1thenBackquoteelseBackquotes(n-2)|'\''->letn=n_occcinifn=1thenQuoteelseQuotes(n-2)|'"'->letn=n_occcinifn=1thenDoublequoteelseDoublequotes(n-2)|'\\'->letn=n_occcinifn=1thenBackslashelseBackslashs(n-2)|'_'->letn=n_occcinifn=1thenUnderscoreelseUnderscores(n-2)|'['->letn=n_occcinifn=1thenObracketelseObrackets(n-2)|']'->letn=n_occcinifn=1thenCbracketelseCbrackets(n-2)|'{'->letn=n_occcinifn=1thenObraceelseObraces(n-2)|'}'->letn=n_occcinifn=1thenCbraceelseCbraces(n-2)|'('->letn=n_occcinifn=1thenOparenthesiselseOparenthesiss(n-2)|')'->letn=n_occcinifn=1thenCparenthesiselseCparenthesiss(n-2)|':'->letn=n_occcinifn=1thenColonelseColons(n-2)|';'->letn=n_occcinifn=1thenSemicolonelseSemicolons(n-2)|'>'->letn=n_occcinifn=1thenGreaterthanelseGreaterthans(n-2)|'~'->letn=n_occcinifn=1thenTildeelseTildes(n-2)|'<'->letn=n_occcinifn=1thenLessthanelseLessthans(n-2)|'@'->letn=n_occcinifn=1thenAtelseAts(n-2)|'&'->letn=n_occcinifn=1thenAmpersandelseAmpersands(n-2)|'|'->letn=n_occcinifn=1thenBarelseBars(n-2)|'^'->letn=n_occcinifn=1thenCaretelseCarets(n-2)|','->letn=n_occcinifn=1thenCommaelseCommas(n-2)|'.'->letn=n_occcinifn=1thenDotelseDots(n-2)|'/'->letn=n_occcinifn=1thenSlashelseSlashs(n-2)|'$'->letn=n_occcinifn=1thenDollarelseDollars(n-2)|'%'->letn=n_occcinifn=1thenPercentelsePercents(n-2)|'='->letn=n_occcinifn=1thenEqualelseEquals(n-2)|'!'->letn=n_occcinifn=1thenExclamationelseExclamations(n-2)|'?'->letn=n_occcinifn=1thenQuestionelseQuestions(n-2)|'0'..'9'->maybe_number()|c->word()inresult:=w::!resultdone;List.rev!resultendmoduleLex_string=Lex(StringLabels)letlex=Lex_string.lextypebigstring=(char,Bigarray.int8_unsigned_elt,Bigarray.c_layout)Bigarray.Array1.tmoduleBigarray_input:Inputwithtypet=bigstring=structmoduleBA=Bigarraytypet=bigstringletget=BA.Array1.getletlength=BA.Array1.dimletsubarr~pos~len=iflen<0||pos<0||pos+len>BA.Array1.dimarrtheninvalid_arg"Bigarray_input.sub";lets=Bytes.createleninfori=0tolen-1doBytes.unsafe_setsi(BA.Array1.unsafe_getarr(i+pos))done;Bytes.unsafe_to_stringsendmoduleLex_bigarray=Lex(Bigarray_input)letlex_bigarray=Lex_bigarray.lexletmake_space=function|0->invalid_arg"Omd_lexer.make_space"|1->Space|n->ifn<0theninvalid_arg"Omd_lexer.make_space"elseSpaces(n-2)(*
(** [string_of_tl l] returns the string representation of l.
[estring_of_tl l] returns the escaped string representation of l
(same semantics as [String.escaped (string_of_tl l)]). *)
let string_of_tl, estring_of_tl =
let g escaped tl =
let b = Buffer.create 42 in
let rec loop : 'a t list -> unit = function
| e::tl ->
Buffer.add_string b (if escaped then String.escaped (string_of_t e)
else string_of_t e);
loop tl
| [] ->
()
in
Buffer.contents (loop tl; b)
in g false, g true
*)letstring_of_tokenstl=letb=Buffer.create128inList.iter(fune->Buffer.add_stringb(string_of_tokene))tl;Buffer.contentsbletdestring_of_tokens?(limit=max_int)tl=letb=Buffer.create1024inletrecloop(i:int)(tlist:toklist):unit=matchtlistwith|e::tl->iflimit=ithenloopi[]elsebeginBuffer.add_stringb(String.escaped(string_of_tokene));Buffer.add_stringb"::";loop(succi)tlend|[]->Buffer.add_stringb"[]"inBuffer.contents(loop0tl;b)