(************************************************************************) (* * The Coq Proof Assistant / The Coq Development Team *) (* v * Copyright INRIA, CNRS and contributors *) (* Tok.t LStream.t -> int option let rec contiguous n m strm = n == m || let (_, ep) = Loc.unloc (LStream.get_loc n strm) in let (bp, _) = Loc.unloc (LStream.get_loc (n + 1) strm) in Int.equal ep bp && contiguous (succ n) m strm let check_no_space m strm = let n = LStream.count strm in if contiguous n (n+m-1) strm then Some m else None let to_entry s (lk : t) = let run strm = match lk 0 strm with None -> err () | Some _ -> () in Entry.(of_parser s { parser_fun = run }) let (>>) (lk1 : t) lk2 n strm = match lk1 n strm with | None -> None | Some n -> lk2 n strm let (<+>) (lk1 : t) lk2 n strm = match lk1 n strm with | None -> lk2 n strm | Some n -> Some n let lk_empty n strm = Some n let lk_kw kw n strm = match LStream.peek_nth n strm with | Tok.KEYWORD kw' | Tok.IDENT kw' -> if String.equal kw kw' then Some (n + 1) else None | _ -> None let lk_kws kws n strm = match LStream.peek_nth n strm with | Tok.KEYWORD kw | Tok.IDENT kw -> if List.mem_f String.equal kw kws then Some (n + 1) else None | _ -> None let lk_ident n strm = match LStream.peek_nth n strm with | Tok.IDENT _ -> Some (n + 1) | _ -> None let lk_ident_except idents n strm = match LStream.peek_nth n strm with | Tok.IDENT ident when not (List.mem_f String.equal ident idents) -> Some (n + 1) | _ -> None let lk_nat n strm = match LStream.peek_nth n strm with | Tok.NUMBER p when NumTok.Unsigned.is_nat p -> Some (n + 1) | _ -> None let rec lk_list lk_elem n strm = ((lk_elem >> lk_list lk_elem) <+> lk_empty) n strm let lk_ident_list = lk_list lk_ident end (** Grammar extensions *) (** NB: [extend_statement = gram_position option * single_extend_statement list] and [single_extend_statement = string option * gram_assoc option * production_rule list] and [production_rule = symbol list * action] In [single_extend_statement], first two parameters are name and assoc iff a level is created *) (** Type of reinitialization data *) type gram_reinit = Gramlib.Gramext.g_assoc * Gramlib.Gramext.position type extend_rule = | ExtendRule : 'a Entry.t * 'a extend_statement -> extend_rule | ExtendRuleReinit : 'a Entry.t * gram_reinit * 'a extend_statement -> extend_rule module EntryCommand = Dyn.Make () module EntryData = struct type _ t = Ex : 'b Entry.t String.Map.t -> ('a * 'b) t end module EntryDataMap = EntryCommand.Map(EntryData) type ext_kind = | ByGrammar of extend_rule | ByEXTEND of (unit -> unit) * (unit -> unit) | ByEntry : ('a * 'b) EntryCommand.tag * string * 'b Entry.t -> ext_kind (** The list of extensions *) let camlp5_state = ref [] let camlp5_entries = ref EntryDataMap.empty (** Deletion *) let grammar_delete e { pos; data } = List.iter (fun (n,ass,lev) -> List.iter (fun pil -> safe_delete_rule e pil) (List.rev lev)) (List.rev data) let grammar_delete_reinit e reinit ({ pos; data } as d)= grammar_delete e d; let a, ext = reinit in let lev = match pos with | Some (Gramext.Level n) -> n | _ -> assert false in let ext = { pos = Some ext; data = [Some lev,Some a,[]] } in safe_extend e ext (** Extension *) let grammar_extend e ext = let undo () = grammar_delete e ext in let redo () = safe_extend e ext in camlp5_state := ByEXTEND (undo, redo) :: !camlp5_state; redo () let grammar_extend_sync e ext = camlp5_state := ByGrammar (ExtendRule (e, ext)) :: !camlp5_state; safe_extend e ext let grammar_extend_sync_reinit e reinit ext = camlp5_state := ByGrammar (ExtendRuleReinit (e, reinit, ext)) :: !camlp5_state; safe_extend e ext (** Remove extensions [n] is the number of extended entries (not the number of Grammar commands!) to remove. *) let rec remove_grammars n = if n>0 then match !camlp5_state with | [] -> anomaly ~label:"Pcoq.remove_grammars" (Pp.str "too many rules to remove.") | ByGrammar (ExtendRuleReinit (g, reinit, ext)) :: t -> grammar_delete_reinit g reinit ext; camlp5_state := t; remove_grammars (n-1) | ByGrammar (ExtendRule (g, ext)) :: t -> grammar_delete g ext; camlp5_state := t; remove_grammars (n-1) | ByEXTEND (undo,redo)::t -> undo(); camlp5_state := t; remove_grammars n; redo(); camlp5_state := ByEXTEND (undo,redo) :: !camlp5_state | ByEntry (tag, name, e) :: t -> Unsafe.clear_entry e; camlp5_state := t; let EntryData.Ex entries = try EntryDataMap.find tag !camlp5_entries with Not_found -> EntryData.Ex String.Map.empty in let entries = String.Map.remove name entries in camlp5_entries := EntryDataMap.add tag (EntryData.Ex entries) !camlp5_entries; remove_grammars (n - 1) let make_rule r = [None, None, r] (** An entry that checks we reached the end of the input. *) (* used by the Tactician plugin *) let eoi_entry en = let e = Entry.make ((Entry.name en) ^ "_eoi") in let symbs = Rule.next (Rule.next Rule.stop (Symbol.nterm en)) (Symbol.token Tok.PEOI) in let act = fun _ x loc -> x in let ext = { pos = None; data = make_rule [Production.make symbs act] } in safe_extend e ext; e (* Parse a string, does NOT check if the entire string was read (use eoi_entry) *) let parse_string f ?source x = let strm = Stream.of_string x in Entry.parse f (Parsable.make ?source strm) (* universes not used by Coq build but still used by some plugins *) type gram_universe = string let utables : (string, unit) Hashtbl.t = Hashtbl.create 97 let create_universe u = let () = Hashtbl.add utables u () in u let uprim = create_universe "prim" [@@deprecated "Deprecated in 8.13"] let uconstr = create_universe "constr" [@@deprecated "Deprecated in 8.13"] let utactic = create_universe "tactic" [@@deprecated "Deprecated in 8.13"] let get_univ u = if Hashtbl.mem utables u then u else raise Not_found let new_entry _ s = let e = Entry.make s in e module GrammarObj = struct type ('r, _, _) obj = 'r Entry.t let name = "grammar" let default _ = None end module Grammar = Register(GrammarObj) let warn_deprecated_intropattern = let open CWarnings in create ~name:"deprecated-intropattern-entry" ~category:"deprecated" (fun () -> Pp.strbrk "Entry name intropattern has been renamed in order \ to be consistent with the documented grammar of tactics. Use \ \"simple_intropattern\" instead.") let check_compatibility = function | Genarg.ExtraArg s when ArgT.repr s = "intropattern" -> warn_deprecated_intropattern () | _ -> () let register_grammar = Grammar.register0 let genarg_grammar x = check_compatibility x; Grammar.obj x let create_generic_entry2 (type a) s (etyp : a raw_abstract_argument_type) : a Entry.t = let e = Entry.create s in let Rawwit t = etyp in let () = Grammar.register0 t e in e let create_generic_entry (type a) _ s (etyp : a raw_abstract_argument_type) : a Entry.t = create_generic_entry2 s etyp (* Initial grammar entries *) module Prim = struct (* Entries that can be referred via the string -> Entry.t table *) (* Typically for tactic or vernac extensions *) let preident = Entry.create "preident" let ident = Entry.create "ident" let natural = Entry.create "natural" let integer = Entry.create "integer" let bignat = Entry.create "bignat" let bigint = Entry.create "bigint" let string = Entry.create "string" let lstring = Entry.create "lstring" let reference = Entry.create "reference" let by_notation = Entry.create "by_notation" let smart_global = Entry.create "smart_global" let strategy_level = Entry.create "strategy_level" (* parsed like ident but interpreted as a term *) let hyp = Entry.create "hyp" let var = hyp let name = Entry.create "name" let identref = Entry.create "identref" let univ_decl = Entry.create "univ_decl" let ident_decl = Entry.create "ident_decl" let pattern_ident = Entry.create "pattern_ident" let pattern_identref = pattern_ident (* To remove in 8.14 *) (* A synonym of ident - maybe ident will be located one day *) let base_ident = Entry.create "base_ident" let qualid = Entry.create "qualid" let fullyqualid = Entry.create "fullyqualid" let dirpath = Entry.create "dirpath" let ne_string = Entry.create "ne_string" let ne_lstring = Entry.create "ne_lstring" let bar_cbrace = Entry.create "'|}'" end module Constr = struct (* Entries that can be referred via the string -> Entry.t table *) let constr = Entry.create "constr" let term = Entry.create "term" let operconstr = term let constr_eoi = eoi_entry constr let lconstr = Entry.create "lconstr" let binder_constr = Entry.create "binder_constr" let ident = Entry.create "ident" let global = Entry.create "global" let universe_name = Entry.create "universe_name" let universe_level = Entry.create "universe_level" let sort = Entry.create "sort" let sort_family = Entry.create "sort_family" let pattern = Entry.create "pattern" let constr_pattern = Entry.create "constr_pattern" let cpattern = Entry.create "cpattern" let lconstr_pattern = cpattern let closed_binder = Entry.create "closed_binder" let binder = Entry.create "binder" let binders = Entry.create "binders" let open_binders = Entry.create "open_binders" let one_open_binder = Entry.create "one_open_binder" let one_closed_binder = Entry.create "one_closed_binder" let binders_fixannot = Entry.create "binders_fixannot" let typeclass_constraint = Entry.create "typeclass_constraint" let record_declaration = Entry.create "record_declaration" let arg = Entry.create "arg" let appl_arg = arg let type_cstr = Entry.create "type_cstr" end module Module = struct let module_expr = Entry.create "module_expr" let module_type = Entry.create "module_type" end let epsilon_value (type s tr a) f (e : (s, tr, a) Symbol.t) = let r = Production.make (Rule.next Rule.stop e) (fun x _ -> f x) in let entry = Entry.make "epsilon" in let ext = { pos = None; data = [None, None, [r]] } in let () = safe_extend entry ext in try Some (parse_string entry "") with _ -> None (** Synchronized grammar extensions *) module GramState = Store.Make () type 'a grammar_extension = 'a -> GramState.t -> extend_rule list * GramState.t module GrammarCommand = Dyn.Make () module GrammarInterp = struct type 'a t = 'a grammar_extension end module GrammarInterpMap = GrammarCommand.Map(GrammarInterp) let grammar_interp = ref GrammarInterpMap.empty type ('a, 'b) entry_extension = 'a -> GramState.t -> string list * GramState.t module EntryInterp = struct type _ t = Ex : ('a, 'b) entry_extension -> ('a * 'b) t end module EntryInterpMap = EntryCommand.Map(EntryInterp) let entry_interp = ref EntryInterpMap.empty type grammar_entry = | GramExt of int * GrammarCommand.t | EntryExt : int * ('a * 'b) EntryCommand.tag * 'a -> grammar_entry let (grammar_stack : (grammar_entry * GramState.t) list ref) = ref [] type 'a grammar_command = 'a GrammarCommand.tag type ('a, 'b) entry_command = ('a * 'b) EntryCommand.tag let create_grammar_command name interp : _ grammar_command = let obj = GrammarCommand.create name in let () = grammar_interp := GrammarInterpMap.add obj interp !grammar_interp in obj let create_entry_command name (interp : ('a, 'b) entry_extension) : ('a, 'b) entry_command = let obj = EntryCommand.create name in let () = entry_interp := EntryInterpMap.add obj (EntryInterp.Ex interp) !entry_interp in obj let iter_extend_sync = function | ExtendRule (e, ext) -> grammar_extend_sync e ext | ExtendRuleReinit (e, reinit, ext) -> grammar_extend_sync_reinit e reinit ext let extend_grammar_command tag g = let modify = GrammarInterpMap.find tag !grammar_interp in let grammar_state = match !grammar_stack with | [] -> GramState.empty | (_, st) :: _ -> st in let (rules, st) = modify g grammar_state in let () = List.iter iter_extend_sync rules in let nb = List.length rules in grammar_stack := (GramExt (nb, GrammarCommand.Dyn (tag, g)), st) :: !grammar_stack let extend_entry_command (type a) (type b) (tag : (a, b) entry_command) (g : a) : b Entry.t list = let EntryInterp.Ex modify = EntryInterpMap.find tag !entry_interp in let grammar_state = match !grammar_stack with | [] -> GramState.empty | (_, st) :: _ -> st in let (names, st) = modify g grammar_state in let entries = List.map (fun name -> Entry.make name) names in let iter name e = camlp5_state := ByEntry (tag, name, e) :: !camlp5_state; let EntryData.Ex old = try EntryDataMap.find tag !camlp5_entries with Not_found -> EntryData.Ex String.Map.empty in let () = assert (not @@ String.Map.mem name old) in let entries = String.Map.add name e old in camlp5_entries := EntryDataMap.add tag (EntryData.Ex entries) !camlp5_entries in let () = List.iter2 iter names entries in let nb = List.length entries in let () = grammar_stack := (EntryExt (nb, tag, g), st) :: !grammar_stack in entries let find_custom_entry tag name = let EntryData.Ex map = EntryDataMap.find tag !camlp5_entries in String.Map.find name map let extend_dyn_grammar (e, _) = match e with | GramExt (_, (GrammarCommand.Dyn (tag, g))) -> extend_grammar_command tag g | EntryExt (_, tag, g) -> ignore (extend_entry_command tag g) (** Registering extra grammar *) type any_entry = AnyEntry : 'a Entry.t -> any_entry let grammar_names : any_entry list String.Map.t ref = ref String.Map.empty let register_grammars_by_name name grams = grammar_names := String.Map.add name grams !grammar_names let find_grammars_by_name name = try String.Map.find name !grammar_names with Not_found -> let fold (EntryDataMap.Any (tag, EntryData.Ex map)) accu = try AnyEntry (String.Map.find name map) :: accu with Not_found -> accu in EntryDataMap.fold fold !camlp5_entries [] (** Summary functions: the state of the lexer is included in that of the parser. Because the grammar affects the set of keywords when adding or removing grammar rules. *) type frozen_t = (grammar_entry * GramState.t) list * CLexer.keyword_state let freeze ~marshallable : frozen_t = (!grammar_stack, CLexer.get_keyword_state ()) (* We compare the current state of the grammar and the state to unfreeze, by computing the longest common suffixes *) let factorize_grams l1 l2 = if l1 == l2 then ([], [], l1) else List.share_tails l1 l2 let rec number_of_entries accu = function | [] -> accu | ((GramExt (p, _) | EntryExt (p, _, _)), _) :: rem -> number_of_entries (p + accu) rem let unfreeze (grams, lex) = let (undo, redo, common) = factorize_grams !grammar_stack grams in let n = number_of_entries 0 undo in remove_grammars n; grammar_stack := common; CLexer.set_keyword_state lex; List.iter extend_dyn_grammar (List.rev redo) (** No need to provide an init function : the grammar state is statically available, and already empty initially, while the lexer state should not be reset, since it contains keywords declared in g_*.mlg *) let parser_summary_tag = Summary.declare_summary_tag "GRAMMAR_LEXER" { Summary.freeze_function = freeze; Summary.unfreeze_function = unfreeze; Summary.init_function = Summary.nop } let with_grammar_rule_protection f x = let fs = freeze ~marshallable:false in try let a = f x in unfreeze fs; a with reraise -> let reraise = Exninfo.capture reraise in let () = unfreeze fs in Exninfo.iraise reraise (** Registering grammar of generic arguments *) let () = let open Stdarg in Grammar.register0 wit_nat (Prim.natural); Grammar.register0 wit_int (Prim.integer); Grammar.register0 wit_string (Prim.string); Grammar.register0 wit_pre_ident (Prim.preident); Grammar.register0 wit_ident (Prim.ident); Grammar.register0 wit_hyp (Prim.hyp); Grammar.register0 wit_ref (Prim.reference); Grammar.register0 wit_smart_global (Prim.smart_global); Grammar.register0 wit_sort_family (Constr.sort_family); Grammar.register0 wit_constr (Constr.constr); ()