(* -*- camlp4r -*- *) (****************************************************************************) (* *) (* OCaml *) (* *) (* INRIA Rocquencourt *) (* *) (* Copyright 2006 Institut National de Recherche en Informatique et *) (* en Automatique. All rights reserved. This file is distributed under *) (* the terms of the GNU Library General Public License, with the special *) (* exception on linking described in LICENSE at the top of the Camlp4 *) (* source tree. *) (* *) (****************************************************************************) (* Authors: * - Daniel de Rauglaudre: initial version * - Nicolas Pouillard: refactoring *) module Make (Structure : Structure.S) = struct module Tools = Tools.Make Structure; module Parser = Parser.Make Structure; open Structure; open Format; open Sig.Grammar; value is_before s1 s2 = match (s1, s2) with [ (Skeyword _ | Stoken _, Skeyword _ | Stoken _) -> False | (Skeyword _ | Stoken _, _) -> True | _ -> False ] ; value rec derive_eps = fun [ Slist0 _ | Slist0sep _ _ | Sopt _ -> True | Stry s -> derive_eps s | Stree t -> tree_derive_eps t | Slist1 _ | Slist1sep _ _ | Stoken _ | Skeyword _ -> (* For sure we cannot derive epsilon from these *) False | Smeta _ _ _ | Snterm _ | Snterml _ _ | Snext | Sself -> (* Approximation *) False ] and tree_derive_eps = fun [ LocAct _ _ -> True | Node {node = s; brother = bro; son = son} -> derive_eps s && tree_derive_eps son || tree_derive_eps bro | DeadEnd -> False ] ; value empty_lev lname assoc = let assoc = match assoc with [ Some a -> a | None -> LeftA ] in {assoc = assoc; lname = lname; lsuffix = DeadEnd; lprefix = DeadEnd} ; value change_lev entry lev n lname assoc = let a = match assoc with [ None -> lev.assoc | Some a -> do { if a <> lev.assoc && entry.egram.warning_verbose.val then do { eprintf " Changing associativity of level \"%s\"\n" n; flush Pervasives.stderr } else (); a } ] in do { match lname with [ Some n -> if lname <> lev.lname && entry.egram.warning_verbose.val then do { eprintf " Level label \"%s\" ignored\n" n; flush Pervasives.stderr } else () | None -> () ]; {assoc = a; lname = lev.lname; lsuffix = lev.lsuffix; lprefix = lev.lprefix} } ; value change_to_self entry = fun [ Snterm e when e == entry -> Sself | x -> x ] ; value get_level entry position levs = match position with [ Some First -> ([], empty_lev, levs) | Some Last -> (levs, empty_lev, []) | Some (Level n) -> let rec get = fun [ [] -> do { eprintf "No level labelled \"%s\" in entry \"%s\"\n" n entry.ename; flush Pervasives.stderr; failwith "Grammar.extend" } | [lev :: levs] -> if Tools.is_level_labelled n lev then ([], change_lev entry lev n, levs) else let (levs1, rlev, levs2) = get levs in ([lev :: levs1], rlev, levs2) ] in get levs | Some (Before n) -> let rec get = fun [ [] -> do { eprintf "No level labelled \"%s\" in entry \"%s\"\n" n entry.ename; flush Pervasives.stderr; failwith "Grammar.extend" } | [lev :: levs] -> if Tools.is_level_labelled n lev then ([], empty_lev, [lev :: levs]) else let (levs1, rlev, levs2) = get levs in ([lev :: levs1], rlev, levs2) ] in get levs | Some (After n) -> let rec get = fun [ [] -> do { eprintf "No level labelled \"%s\" in entry \"%s\"\n" n entry.ename; flush Pervasives.stderr; failwith "Grammar.extend" } | [lev :: levs] -> if Tools.is_level_labelled n lev then ([lev], empty_lev, levs) else let (levs1, rlev, levs2) = get levs in ([lev :: levs1], rlev, levs2) ] in get levs | None -> match levs with [ [lev :: levs] -> ([], change_lev entry lev "", levs) | [] -> ([], empty_lev, []) ] ] ; value rec check_gram entry = fun [ Snterm e -> if e.egram != entry.egram then do { eprintf "\ Error: entries \"%s\" and \"%s\" do not belong to the same grammar.\n" entry.ename e.ename; flush Pervasives.stderr; failwith "Grammar.extend error" } else () | Snterml e _ -> if e.egram != entry.egram then do { eprintf "\ Error: entries \"%s\" and \"%s\" do not belong to the same grammar.\n" entry.ename e.ename; flush Pervasives.stderr; failwith "Grammar.extend error" } else () | Smeta _ sl _ -> List.iter (check_gram entry) sl | Slist0sep s t -> do { check_gram entry t; check_gram entry s } | Slist1sep s t -> do { check_gram entry t; check_gram entry s } | Slist0 s | Slist1 s | Sopt s | Stry s -> check_gram entry s | Stree t -> tree_check_gram entry t | Snext | Sself | Stoken _ | Skeyword _ -> () ] and tree_check_gram entry = fun [ Node {node = n; brother = bro; son = son} -> do { check_gram entry n; tree_check_gram entry bro; tree_check_gram entry son } | LocAct _ _ | DeadEnd -> () ] ; value get_initial = fun [ [Sself :: symbols] -> (True, symbols) | symbols -> (False, symbols) ] ; value insert_tokens gram symbols = let rec insert = fun [ Smeta _ sl _ -> List.iter insert sl | Slist0 s | Slist1 s | Sopt s | Stry s -> insert s | Slist0sep s t -> do { insert s; insert t } | Slist1sep s t -> do { insert s; insert t } | Stree t -> tinsert t | Skeyword kwd -> using gram kwd | Snterm _ | Snterml _ _ | Snext | Sself | Stoken _ -> () ] and tinsert = fun [ Node {node = s; brother = bro; son = son} -> do { insert s; tinsert bro; tinsert son } | LocAct _ _ | DeadEnd -> () ] in List.iter insert symbols ; value insert_tree entry gsymbols action tree = let rec insert symbols tree = match symbols with [ [s :: sl] -> insert_in_tree s sl tree | [] -> match tree with [ Node {node = s; son = son; brother = bro} -> Node {node = s; son = son; brother = insert [] bro} | LocAct old_action action_list -> let () = if entry.egram.warning_verbose.val then eprintf " Grammar extension: in [%s] some rule has been masked@." entry.ename else () in LocAct action [old_action :: action_list] | DeadEnd -> LocAct action [] ] ] and insert_in_tree s sl tree = match try_insert s sl tree with [ Some t -> t | None -> Node {node = s; son = insert sl DeadEnd; brother = tree} ] and try_insert s sl tree = match tree with [ Node {node = s1; son = son; brother = bro} -> if Tools.eq_symbol s s1 then let t = Node {node = s1; son = insert sl son; brother = bro} in Some t else if is_before s1 s || derive_eps s && not (derive_eps s1) then let bro = match try_insert s sl bro with [ Some bro -> bro | None -> Node {node = s; son = insert sl DeadEnd; brother = bro} ] in let t = Node {node = s1; son = son; brother = bro} in Some t else match try_insert s sl bro with [ Some bro -> let t = Node {node = s1; son = son; brother = bro} in Some t | None -> None ] | LocAct _ _ | DeadEnd -> None ] in insert gsymbols tree ; value insert_level entry e1 symbols action slev = match e1 with [ True -> {assoc = slev.assoc; lname = slev.lname; lsuffix = insert_tree entry symbols action slev.lsuffix; lprefix = slev.lprefix} | False -> {assoc = slev.assoc; lname = slev.lname; lsuffix = slev.lsuffix; lprefix = insert_tree entry symbols action slev.lprefix} ] ; value levels_of_rules entry position rules = let elev = match entry.edesc with [ Dlevels elev -> elev | Dparser _ -> do { eprintf "Error: entry not extensible: \"%s\"\n" entry.ename; flush Pervasives.stderr; failwith "Grammar.extend" } ] in if rules = [] then elev else let (levs1, make_lev, levs2) = get_level entry position elev in let (levs, _) = List.fold_left (fun (levs, make_lev) (lname, assoc, level) -> let lev = make_lev lname assoc in let lev = List.fold_left (fun lev (symbols, action) -> let symbols = List.map (change_to_self entry) symbols in do { List.iter (check_gram entry) symbols; let (e1, symbols) = get_initial symbols; insert_tokens entry.egram symbols; insert_level entry e1 symbols action lev }) lev level in ([lev :: levs], empty_lev)) ([], make_lev) rules in levs1 @ List.rev levs @ levs2 ; value extend entry (position, rules) = let elev = levels_of_rules entry position rules in do { entry.edesc := Dlevels elev; entry.estart := fun lev strm -> let f = Parser.start_parser_of_entry entry in do { entry.estart := f; f lev strm }; entry.econtinue := fun lev bp a strm -> let f = Parser.continue_parser_of_entry entry in do { entry.econtinue := f; f lev bp a strm } }; end;