printfの自作
Posted feedbacks - OCaml
超手抜き実装。単に int, float, char, string を単純に変換して埋め込むだけのものです。 OCaml ではコンパイラをいじらないと、まともに使えるものはできっこないので、こんなもんで良いんです。 3 ファイルを同じディレクトリに置き、 % ocamlbuild tiny_printf.cma などとします。 % ocaml -I _build tiny_printf.cma # let r = Obj.repr;; val r : 'a -> Obj.t = <fun> # Tiny_printf.sprintf "hoge %% %d %s %c %f" [r 10; r "fuga"; r 'C'; r 2.1];; - : string = "hoge % 10 fuga C 2.1"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | /* parser.mly */
%token <string> CONV
%token <string> STR
%start expr
%type <string> expr
%%
expr:
CONV { $1 }
| STR { $1 }
;
(* lexer.mll *)
{
open Parser
exception Eof
}
let conv = ['d' 'f' 'c' 's']
rule token = parse
('%' (conv | '%')) as str { CONV(str) }
| ([^'%'] +) as str { STR(str) }
| eof { raise Eof }
(* tiny_printf.ml *)
let sprintf format_str args =
let strbuf = Buffer.create 10 in
let add = Buffer.add_string strbuf in
let lexbuf = Lexing.from_string format_str in
let rec loop args' =
match Parser.expr Lexer.token lexbuf with
| "%%" -> (add "%"; loop args')
| s when s.[0] = '%' -> begin
let arg, rest =
match args' with
| x::xs -> x, xs
| _ -> failwith "number of args mismatch!"
in
add begin
match s with
| "%%" -> "%"
| "%d" when Obj.is_int arg ->
string_of_int (Obj.obj arg)
| "%f" when Obj.tag arg = Obj.double_tag ->
string_of_float (Obj.obj arg)
| "%c" when Obj.is_int arg ->
String.make 1 (Obj.obj arg)
| "%s" when Obj.tag arg = Obj.string_tag ->
Obj.obj arg
| _ ->
failwith "type mismatch!"
end;
loop rest
end
| s -> (add s; loop args')
in
try loop args
with Lexer.Eof -> Buffer.contents strbuf
|
OCaml で、サポートする書式指定は #4409 の jijixi さんのと大体同じですが、ちゃんと型チェックする版。
基本は参考ページの手法を使って、でもそれだけだと printf っぽく見えないので Camlp4 を被せました。
KURO-BOX% ocaml
Objective Caml version 3.09.2
# #load "camlp4o.cma";;
Camlp4 Parsing version 3.09.2
# #load "pa_printf.cmo";;
# myprintf "hoge %% %d %s %c %f" 10 "fuga" 'C' 2.1;;
- : string = "hoge % 10 fuga C 2.1"
# myprintf "hoge %% %d %s %c %f" 3.14 "fuga" 'C' 2.1;;
This expression has type float but is here used with type int
see: Olivier Danvy (1998) "Functional Unparsing"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | (* ocamlc -c -I +camlp4 -pp 'camlp4o pa_extend.cmo q_MLast.cmo' pa_printf.ml *)
let make_printf _loc format =
let parse_format format =
let rec lit e s = parser
| [< ''%'; strm >] -> esc <:expr< compose $e$ (lit $str:s$) >> "" strm
| [< 'c; strm >] -> lit e (s ^ String.make 1 c) strm
| [< >] -> <:expr< compose $e$ (lit $str:s$) >>
and esc e s = parser
| [< ''d'; strm >] -> lit <:expr< compose $e$ int >> "" strm
| [< ''f'; strm >] -> lit <:expr< compose $e$ float >> "" strm
| [< ''c'; strm >] -> lit <:expr< compose $e$ char >> "" strm
| [< ''s'; strm >] -> lit <:expr< compose $e$ str >> "" strm
| [< ''%'; strm >] -> lit <:expr< compose $e$ (lit "%") >> "" strm
in
lit <:expr< fun x -> x >> "" (Stream.of_string format)
in
let parsed_format = parse_format format in
<:expr<
let compose f g x = f (g x) in
let lit x k s = k (s ^ x) in
let int k s x = k (s ^ string_of_int x) in
let str k s x = k (s ^ x) in
let float k s x = k (s ^ string_of_float x) in
let char k s x = k (s ^ String.make 1 x) in
let format p = p (fun s -> s) "" in
format $parsed_format$
>>
;;
EXTEND
Pcaml.expr: LEVEL "expr1" [
[ "myprintf"; format = STRING -> make_printf _loc format ]
];
END
|



yappy
#4119()
[
C
]
Rating-4/18=-0.22
Rating-4/18=-0.22-0+
[ reply ]