12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758openCoreopenMParser(** Assumes the left and right delimiter are the same, and that these can be
escaped. Does not parse a string body containing newlines (as usual when
escaping with \n) *)moduleEscapable=structmoduletypeS=sigvaldelimiter:stringvalescape:charendmoduleMake(M:S)=struct(* delimiters can be escaped and parsing continues within the string body *)letescaped_char_ss=any_charsletchar_token_ss=((charM.escape>>escaped_char_s>>=func->return(Format.sprintf{|%c%c|}M.escapec))<|>(any_char|>>String.of_char))sletbase_string_literals=((stringM.delimiter>>(many_untilchar_token_s(stringM.delimiter))|>>String.concat)>>=funresult->return(Format.sprintf{|%s%s%s|}M.delimiterresultM.delimiter))sendend(** Quoted or raw strings. Allows different left and right delimiters, and
disallows any sort of escaping. Does not support raw strings with identifiers
yet, e.g., {blah|<string body>|blah} (OCaml) or delim`<string body>`delim
syntax (Go) *)moduleRaw=structmoduletypeS=sigvalleft_delimiter:stringvalright_delimiter:stringendmoduleMake(M:S)=structletchar_token_ss=(any_char_or_nl|>>String.of_char)sletbase_string_literals=((stringM.left_delimiter>>(many_untilchar_token_s(stringM.right_delimiter))|>>String.concat<?>"raw string literal body")>>=funresult->return(Format.sprintf{|%s%s%s|}M.left_delimiterresultM.right_delimiter))sendend