Release coccinelle-0.1.2
[bpt/coccinelle.git] / commons / parser_combinators.ml
diff --git a/commons/parser_combinators.ml b/commons/parser_combinators.ml
new file mode 100644 (file)
index 0000000..0e44cb8
--- /dev/null
@@ -0,0 +1,370 @@
+(*****************************************************************************)
+(*  *)
+(*****************************************************************************)
+(* src: Jon Harrop. 
+ *
+ * "Certain applications are extremely well suited to functional
+ * programming and parsing is one of them. Specifically, the ability to
+ * write functional combinators that allow parsers for everything from
+ * integers up to symbolic expressions to be composed is more general
+ * and provides more opportunity for code reuse than the use of
+ * conventional parser generators such as ocamllex and ocamlyacc. This
+ * article explains how parser combinators may be designed and
+ * implemented in OCaml, using the standard example of a calculator."
+ * 
+ * Based on haskell articles I guess like meijer functional pearl or
+ * graham hutton articles. Also maybe based on haskell parsec.
+ * 
+ * pad: a few bugfix. I also put more restrictive and descriptive types.
+ * pad: I remember having coded such a library, maybe not in ocaml. 
+ * Or maybe it was during a "TP compilation" at INSA ? I remember having
+ * a generic lexer. Or maybe it was genlex ?
+ * 
+ * 
+ * 
+ * 
+ * alternatives: genlex + parser extension of ocaml (streams).
+ * cf genlex doc:
+ * 
+ * Example: a lexer suitable for a desk calculator is obtained by
+ * let lexer = make_lexer ["+";"-";"*";"/";"let";"="; "("; ")"]  
+ * let parse_expr = parser
+ *     [< 'Int n >] -> n
+ *   | [< 'Kwd "("; n = parse_expr; 'Kwd ")" >] -> n
+ *   | [< n1 = parse_expr; n2 = parse_remainder n1 >] -> n2
+ * and parse_remainder n1 = parser
+ *   [< 'Kwd "+"; n2 = parse_expr >] -> n1+n2
+ *   | ...
+ * type token =
+ * |   Kwd of string
+ * |   Ident of string
+ * |   Int of int
+ * |   Float of float
+ * |   String of string
+ * |   Char of char
+ * 
+ * 
+ * Cf also ocaml manual
+ *  let rec parse_expr = parser
+ *      [< e1 = parse_mult; e = parse_more_adds e1 >] -> e
+ *  and parse_more_adds e1 = parser
+ *      [< 'Kwd "+"; e2 = parse_mult; e = parse_more_adds (Sum(e1, e2)) >] -> e
+ *    | [< 'Kwd "-"; e2 = parse_mult; e = parse_more_adds (Diff(e1, e2)) >] -> e
+ *    | [< >] -> e1
+ *  and parse_mult = parser
+ *      [< e1 = parse_simple; e = parse_more_mults e1 >] -> e
+ *  and parse_more_mults e1 = parser
+ *      [< 'Kwd "*"; e2 = parse_simple; e = parse_more_mults (Prod(e1, e2)) >] -> e
+ *    | [< 'Kwd "/"; e2 = parse_simple; e = parse_more_mults (Quot(e1, e2)) >] -> e
+ *    | [< >] -> e1
+ *  and parse_simple = parser
+ *      [< 'Ident s >] -> Var s
+ *    | [< 'Int i >] -> Const(float i)
+ *    | [< 'Float f >] -> Const f
+ *    | [< 'Kwd "("; e = parse_expr; 'Kwd ")" >] -> e;;
+ * But see how they are forced to use a LL(1) grammar which denatures the
+ * grammar "parse_more_xxx"
+ * 
+ *)
+
+(*****************************************************************************)
+(* Parser Combinators *)
+(*****************************************************************************)
+
+(* src: Jon Harrop. pad: a few bugfix *)
+
+type ('a, 'b) genp = 'a list -> 'b * 'a list
+let val_of_parser = fst
+
+(* lexer = parser of char list *)
+(* type 'a lexer = (char, 'a) genp *)
+
+(* grammer = parser ot tokens *)
+(* type 'a p = (token, 'a) genp *)
+
+
+(* pad: could also do it by returning a Maybe and use monad *)
+let ( ||| ) p1 p2 s =
+  try 
+    p1 s 
+  with Not_found -> 
+    p2 s
+      
+let ( +++ ) p1 p2 s =
+  let e1, s = p1 s in
+  let e2, s = p2 s in
+  (e1, e2), s
+
+let rec many p s =
+  try
+    let e, s = p s in
+    let es, s = many p s in
+    e::es, s
+  with Not_found ->
+    [], s
+
+
+let ( >| ) p k i =
+  let e, s = p i in
+  k e, s
+
+(* was called 'some', but confusing *)
+let pred p = function
+  | h::t when p h -> h, t
+  | _ -> raise Not_found
+
+let a x = pred (( = ) x)
+
+let several p = many (pred p)
+
+
+module Abstr : sig
+    type t
+    val x : t
+  end = struct
+    type t = int
+    let x = 0
+  end
+
+let fin = function
+  | [] as t -> Abstr.x, t
+  | _ -> raise Not_found
+
+
+(*****************************************************************************)
+(* Lexing *)
+(*****************************************************************************)
+(* a generic lexer *)
+
+let digit = function
+  | '0'..'9' -> true
+  | _ -> false
+
+let alpha = function
+  | 'a'..'z' | 'A'..'Z' -> true
+  | _ -> false
+
+
+let symbol = function
+  | '(' | ')' 
+  | '{' | '}' 
+  | '[' | ']' 
+  | '<' | '>' 
+  | '+' | '-' | '*' | '/' 
+  | '&' | '|' | '!' 
+
+  | '=' | '~' | '@'
+      -> true
+  | _ -> false
+
+let space = function
+  | ' ' | '\t' | '\n' -> true
+  | _ -> false
+
+let stringquote = function
+  | '"' -> true
+  | _ -> false
+
+let quote = function
+  | '\'' -> true
+  | _ -> false
+
+
+let alphanum c = digit c || alpha c
+
+
+let alphanum_underscore c = digit c || alpha c || (c = '_')
+let alphanum_minus c = digit c || alpha c || (c = '-')
+let alphanum_under_minus c = digit c || alpha c || (c = '-') || (c = '_')
+
+
+
+let (+>) o f = f o
+let string_of_chars cs = 
+  cs +> List.map (String.make 1) +> String.concat ""
+
+
+let collect(h, t) =
+    String.concat "" (List.map (String.make 1) (h::t))
+
+let collectbis(xs) =
+    String.concat "" (List.map (String.make 1) (xs))
+
+let list_of_string string =
+    let list = ref [] in
+    String.iter (fun c -> list := c :: !list) string;
+    List.rev !list
+
+
+(*****************************************************************************)
+(* still generic *)
+(*****************************************************************************)
+
+type token =
+  | IDENT of string
+  | KWD of string
+  | INT of string
+  | SYM of string
+  | STR of string
+
+let string_of_token = function
+  | IDENT string -> "IDENT:" ^ string
+  | KWD string -> "KWD:" ^ string
+  | INT string -> "INT:" ^ string
+  | SYM string -> "SYM:" ^ string
+  | STR string -> "STR:" ^ string
+
+
+type lexer = (char, token) genp
+
+
+let rawnumber =
+  pred digit +++ several digit >| fun x -> INT(collect x)
+let rawident =
+  pred alpha +++ several alphanum >| fun x -> IDENT(collect x)
+let rawsymbol =
+  pred symbol +++ several symbol >| fun x -> SYM(collect x)
+
+let rawkeyword =
+  let p c = not(space c) && not(digit c) in
+  pred p +++ several p >| fun x -> KWD(collect x)
+
+
+(* todo: handle antislash *)
+let rawstring = 
+  pred stringquote +++ 
+  several (fun c -> not (stringquote c)) +++
+  pred stringquote
+   >| (fun ((c1, cs), c3) -> 
+        let s = string_of_chars cs in
+        STR s (* exclude the marker *)
+   )
+
+
+let lex_gen tokenf str = 
+  let alltoks = (many tokenf) +++ fin >| fst in
+  val_of_parser (alltoks (list_of_string str))
+
+let parse_gen tokenf grammarf p string =
+    val_of_parser (grammarf (lex_gen tokenf string))
+
+(*****************************************************************************)
+(* not generic anymore *)
+(*****************************************************************************)
+(* the order is important if some "rules" overlap, as in ocamllex *)
+let token =
+    (rawident ||| rawnumber ||| rawkeyword) +++ several space >| fst
+
+(* pad: bugfix: was not defined in jon harrop article *)
+let tokens = many token
+
+let alltokens =
+    tokens +++ fin >| fst
+
+let lex (string : string) =
+    val_of_parser (alltokens (list_of_string string))
+
+
+
+let test1 () = 
+  Common.example
+  (lex "a x^2 + b x + c" 
+    =
+    [IDENT "a"; IDENT "x"; KWD "^"; INT "2"; KWD "+"; IDENT "b"; IDENT "x";
+     KWD "+"; IDENT "c"]
+  )
+
+(*****************************************************************************)
+(* Parsing *)
+(*****************************************************************************)
+
+type expr =
+    | Int of int
+    | Var of string
+    | Add of expr * expr
+    | Mul of expr * expr
+
+type 'a pparser = (token, 'a) genp
+
+(*
+open Format;;
+# let rec print_expr ff = function
+    | Int n -> fprintf ff "%d" n
+    | Var x -> fprintf ff "%s" x
+    | Add(f, g) ->
+        fprintf ff "%a + %a" print_expr f print_expr g
+    | Mul(f, g) ->
+        fprintf ff "%a %a" print_mul f print_mul g
+  and print_mul ff = function
+    | Add _ as e -> fprintf ff "(%a)" print_expr e
+    | e -> fprintf ff "%a" print_expr e
+#install_printer print_expr
+*)
+
+let ident = function
+    | IDENT x :: t -> x, t
+    | _ -> raise Not_found
+
+let int = function
+    | INT n :: t -> n, t
+    | _ -> raise Not_found
+
+let string = function
+    | STR x :: t -> x, t
+    | _ -> raise Not_found
+
+(* src: Jon Harrop
+ * "This style of parsing, known as recursive descent parsing , has one
+ * important caveat. If a rule tries to match itself immediately, even if
+ * that is succeeded by other parsers, then the resulting program will go
+ * into an infinite loops with the parser for that rule calling itself
+ * indefinitely until a stack overflow occurs. Consequently, our
+ * implementation of the factor parser is careful to parse an atom first,
+ * and term calls factor first, to avoid this problem."
+ * 
+ * pad: bugfix, added the KWD "*".
+ *)
+
+(* pad: I think I remembered you cant eta-factorize the parameter 
+ * when you use mutually recursive
+ *)
+let rec atom s =
+ (
+  (int             >| fun n -> Int(int_of_string n)) 
+  |||
+  (ident           >| fun x -> Var x) 
+  |||
+  (a (KWD "(") +++ term +++ a (KWD ")")     >| fun ((_, e), _) -> e)
+  ) s
+and factor s =
+ (
+  (atom +++ a (KWD "*") +++ factor      >| fun ((f, _), g) -> Mul (f,g)) 
+  |||
+  atom
+ ) s
+and term s =
+  (
+   (factor +++ a (KWD "+") +++ term     >| fun ((f, _), g) -> Add (f,g)) 
+   |||
+   factor
+  ) s
+
+
+let expr =
+    term +++ fin >| fst
+
+let parse p string =
+    val_of_parser(p(lex string))
+
+(* 
+parse expr "a x x + b x + c"
+*)
+
+
+(*****************************************************************************)
+
+module Infix = struct
+  let (|||) = (|||)
+  let (+++) = (+++)
+  let (>|) = (>|)
+end