Skip to content

Commit 914f603

Browse files
author
Jan-Oliver Kaiser
committed
Add support for syntactic command arguments
1 parent 20c57da commit 914f603

12 files changed

+697
-110
lines changed

elpi/coq-arg-HOAS.elpi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ external symbol str : string -> argument. % Eg. x "y" z.w. or any Coq keywo
5454
external symbol trm : term -> argument. % Eg. (t).
5555
external symbol open-trm : int -> term -> argument.
5656

57+
external symbol syntax.arg : syntax.argument -> argument.
58+
5759
% Extra arguments for commands. [Definition], [Axiom], [Record] and [Context]
5860
% take precedence over the [str] argument above (when not "quoted").
5961
%

src/rocq_elpi_arg_HOAS.ml

Lines changed: 274 additions & 59 deletions
Large diffs are not rendered by default.

src/rocq_elpi_arg_HOAS.mli

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,47 @@
55
open Elpi.API.RawData
66
open Rocq_elpi_utils
77

8+
module API = Elpi.API
9+
module CD = Elpi.API.RawOpaqueData
10+
811
type phase = Interp | Synterp | Both
912
type proof =
1013
| Begin of string option (* None = always, Some x only if #[x] present*)
1114
| End
1215

16+
module GS : sig
17+
type 'a t = {
18+
gs : Genintern.glob_sign;
19+
vl : 'a
20+
}
21+
val get : 'a t -> 'a
22+
val mk : Genintern.glob_sign -> 'a -> 'a t
23+
end
24+
25+
module IS : sig
26+
type 'a t = {
27+
is : Geninterp.interp_sign;
28+
gs : Genintern.glob_sign;
29+
vl : 'a
30+
}
31+
val get : 'a t -> 'a
32+
val mk : Geninterp.interp_sign -> Genintern.glob_sign -> 'a -> 'a t
33+
val of_gs : Geninterp.interp_sign -> 'a GS.t -> 'a t
34+
end
35+
1336
module Cmd : sig
1437

1538
type raw_term = Constrexpr.constr_expr
16-
type glob_term = Genintern.glob_constr_and_expr
17-
type top_term = Ltac_plugin.Tacinterp.interp_sign * Genintern.glob_constr_and_expr
39+
type glob_term = raw_term GS.t
40+
type top_term = raw_term IS.t
1841

1942
type raw_record_decl = Vernacentries.Preprocessed_Mind_decl.record
20-
type glob_record_decl = Genintern.glob_sign * raw_record_decl
21-
type top_record_decl = Geninterp.interp_sign * glob_record_decl
43+
type glob_record_decl = raw_record_decl GS.t
44+
type top_record_decl = raw_record_decl IS.t
2245

2346
type raw_indt_decl = Vernacentries.Preprocessed_Mind_decl.inductive
24-
type glob_indt_decl = Genintern.glob_sign * raw_indt_decl
25-
type top_indt_decl = Geninterp.interp_sign * glob_indt_decl
47+
type glob_indt_decl = raw_indt_decl GS.t
48+
type top_indt_decl = raw_indt_decl IS.t
2649

2750
type raw_constant_decl = {
2851
name : qualified_name;
@@ -33,12 +56,12 @@ type raw_constant_decl = {
3356
red : Genredexpr.raw_red_expr option;
3457
}
3558
val pr_raw_constant_decl : Environ.env -> Evd.evar_map -> raw_constant_decl -> Pp.t
36-
type glob_constant_decl = Genintern.glob_sign * raw_constant_decl
37-
type top_constant_decl = Geninterp.interp_sign * glob_constant_decl
59+
type glob_constant_decl = raw_constant_decl GS.t
60+
type top_constant_decl = raw_constant_decl IS.t
3861

3962
type raw_context_decl = Constrexpr.local_binder_expr list
40-
type glob_context_decl = Genintern.glob_sign * raw_context_decl
41-
type top_context_decl = Geninterp.interp_sign * glob_context_decl
63+
type glob_context_decl = raw_context_decl GS.t
64+
type top_context_decl = raw_context_decl IS.t
4265

4366
type ('a,'b,'c,'d,'e) t =
4467
| Int : int -> ('a,'b,'c,'d,'e) t
@@ -125,14 +148,56 @@ val in_elpi_tac_econstr :
125148
Elpi.API.State.t * term list * Elpi.API.Conversion.extra_goals
126149

127150
(* for commands *)
151+
val arg_type : API.Data.term API.Conversion.t
152+
module Syntactic : sig
153+
module Tag :
154+
sig
155+
type 'a t = {
156+
is : Geninterp.interp_sign;
157+
gs : Genintern.glob_sign;
158+
vl : 'a;
159+
tag : int;
160+
}
161+
val compare_tag : 'a t -> 'b t -> int
162+
val counter : int ref
163+
val fresh : 'a IS.t -> 'a t
164+
val drop_tag : 'a t -> 'a IS.t
165+
end
166+
type res_term = Cmd.raw_term Tag.t
167+
type res_record_decl = Cmd.raw_record_decl Tag.t
168+
type res_indt_decl = Cmd.raw_indt_decl Tag.t
169+
type res_constant_decl = Cmd.raw_constant_decl Tag.t
170+
type res_context_decl = Cmd.raw_context_decl Tag.t
171+
type res =
172+
(res_term, res_record_decl, res_indt_decl, res_constant_decl,
173+
res_context_decl)
174+
Cmd.t
175+
val pp_res : Environ.env -> Evd.evar_map -> res -> Pp.t
176+
val trm : res_term CD.cdata
177+
val trm_type : res_term API.Conversion.t
178+
val constant_decl : res_constant_decl CD.cdata
179+
val constant_decl_type : res_constant_decl API.Conversion.t
180+
val indt_decl : res_indt_decl CD.cdata
181+
val indt_decl_type : res_indt_decl API.Conversion.t
182+
val record_decl : res_record_decl CD.cdata
183+
val record_decl_type : res_record_decl API.Conversion.t
184+
val context : res_context_decl CD.cdata
185+
val context_type : res_context_decl API.Conversion.t
186+
val arg_type : res API.Conversion.t
187+
val delimiter_depth : Constrexpr.delimiter_depth API.Conversion.t
188+
val as_normal_arg : API.RawData.constant
189+
val res_of_top : Cmd.top -> res
190+
val top_of_res : res -> Cmd.top
191+
val ml_data : API.BuiltIn.declaration list
192+
end
128193
val in_elpi_cmd :
129194
loc:Loc.t ->
130195
depth:int ->
131196
base:Elpi.API.Compile.program ->
132197
?calldepth:int ->
198+
kind:arg_kind ->
133199
Rocq_elpi_HOAS.empty Rocq_elpi_HOAS.coq_context ->
134200
Elpi.API.State.t ->
135-
raw:bool ->
136201
Cmd.top ->
137202
Elpi.API.State.t * term * Elpi.API.Conversion.extra_goals
138203
val in_elpi_cmd_synterp :

src/rocq_elpi_arg_syntax.mlg

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,21 @@ let skip_and_synterp_attributes = Attributes.Notations.(skip_attribute ++ synter
195195
let scope_and_skip_and_synterp_attributes = Attributes.Notations.(scope_attribute ++ skip_attribute ++ synterp_attribute)
196196

197197
let raw_args_attributes =
198-
Attributes.(qualify_attribute "arguments" (bool_attribute ~name:"raw"))
198+
Attributes.qualify_attribute "arguments"
199+
(Attributes.attribute_of_list
200+
["elaborated", (fun ?loc old -> function
201+
| VernacFlagEmpty -> U.arg_kind_merge ?loc old U.Elaborated
202+
| _ -> CErrors.user_err ?loc Pp.(str "Argument \"elaborated\" does not expect a value"));
203+
"unelaborated", (fun ?loc old -> function
204+
| VernacFlagEmpty -> U.arg_kind_merge ?loc old U.Unelaborated
205+
| _ -> CErrors.user_err ?loc Pp.(str "Argument \"unelaborated\" does not expect a value"));
206+
"raw", (fun ?loc old -> function
207+
| VernacFlagEmpty -> U.arg_kind_merge ?loc old U.Unelaborated
208+
| _ -> CErrors.user_err ?loc Pp.(str "Argument \"raw\" does not expect a value"));
209+
"syntactic", (fun ?loc old -> function
210+
| VernacFlagEmpty -> U.arg_kind_merge ?loc old U.Syntactic
211+
| _ -> CErrors.user_err ?loc Pp.(str "Argument \"syntactic\" does not expect a value"));
212+
])
199213

200214
let validate_attributes a flags =
201215
let extra, raw_args = Attributes.parse_with_extra a flags in

src/rocq_elpi_builtins.ml

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ open Names
2727

2828
open Rocq_elpi_utils
2929
open Rocq_elpi_HOAS
30+
open Rocq_elpi_arg_HOAS
3031

3132
let string_of_ppcmds options pp =
3233
let b = Buffer.create 512 in
@@ -252,6 +253,27 @@ let term_skeleton = {
252253
embed = (fun ~depth _ _ _ _ -> assert false);
253254
}
254255

256+
let type_constraint =
257+
API.AlgebraicData.declare {
258+
ty = TyName "type-constraint";
259+
doc = "The expected type for elaborating syntactic terms";
260+
pp = (fun fmt _ -> Format.fprintf fmt "<TODO>");
261+
constructors = Pretyping.[
262+
K("without-type-constraint", "Pretype without type constraint", N,
263+
B (WithoutTypeConstraint),
264+
M (fun ~ok ~ko -> function WithoutTypeConstraint -> ok | _ -> ko ())
265+
);
266+
K("of-type", "Pretype with a specific expected type", CA(term, N),
267+
B (fun t -> OfType t),
268+
M (fun ~ok ~ko -> function OfType t -> ok t | _ -> ko ())
269+
);
270+
K("is-type", "Pretype as a type", N,
271+
B (IsType),
272+
M (fun ~ok ~ko -> function IsType -> ok | _ -> ko ())
273+
);
274+
];
275+
}
276+
255277
let sealed_goal = {
256278
Conv.ty = Conv.TyName "sealed-goal";
257279
pp_doc = (fun fmt () -> ());
@@ -4390,5 +4412,104 @@ Supported attributes:
43904412
DocAbove)
43914413

43924414
]
4415+
@ Syntactic.ml_data @
4416+
[MLDataC(type_constraint);
4417+
MLCode(Pred("syntax.default-elab",
4418+
In(Syntactic.arg_type, "SyntaxArg",
4419+
Out(Rocq_elpi_arg_HOAS.arg_type, "Arg",
4420+
InOut(B.ioarg B.diagnostic, "Diagnostic",
4421+
Full(global, "Elaborates the syntactic argument with the settings of #[arguments(elaborated)]")))),
4422+
fun sarg _ diag ~depth coq_ctx _csts state ->
4423+
let loc = to_coq_loc @@ State.get Rocq_elpi_builtins_synterp.invocation_site_loc state in
4424+
let base = Option.get (State.get base state) in
4425+
try
4426+
let state, res, extra_goals =
4427+
Syntactic.top_of_res sarg |>
4428+
Rocq_elpi_arg_HOAS.in_elpi_cmd ~loc ~depth ~base ~kind:Elaborated coq_ctx state
4429+
in
4430+
state, (!: res +! B.mkOK), extra_goals
4431+
with e ->
4432+
diag_error_lazy diag @@ fun () ->
4433+
let error =
4434+
string_of_ppcmds coq_ctx.options @@
4435+
try CErrors.print_no_report e with | _ -> raise No_clause
4436+
in
4437+
state, ?: None +! B.mkERROR error, []
4438+
),
4439+
DocAbove);
4440+
MLCode(Pred("syntax.default-unelab",
4441+
In(Syntactic.arg_type, "SyntaxArg",
4442+
Out(Rocq_elpi_arg_HOAS.arg_type, "Arg",
4443+
InOut(B.ioarg B.diagnostic, "Diagnostic",
4444+
Full(global, "Elaborates the syntactic argument with the settings of #[arguments(unelaborated)]")))),
4445+
fun sarg _ diag ~depth coq_ctx _csts state ->
4446+
let loc = to_coq_loc @@ State.get Rocq_elpi_builtins_synterp.invocation_site_loc state in
4447+
let base = Option.get (State.get base state) in
4448+
try
4449+
let state, res, extra_goals =
4450+
Syntactic.top_of_res sarg |>
4451+
Rocq_elpi_arg_HOAS.in_elpi_cmd ~loc ~depth ~base ~kind:Unelaborated coq_ctx state
4452+
in
4453+
state, (!: res +! B.mkOK), extra_goals
4454+
with e ->
4455+
diag_error_lazy diag @@ fun () ->
4456+
let error =
4457+
string_of_ppcmds coq_ctx.options @@
4458+
try CErrors.print_no_report e with | _ -> raise No_clause
4459+
in
4460+
state, ?: None +! B.mkERROR error, []
4461+
),
4462+
DocAbove);
4463+
MLCode(Pred("syntax.push-scope",
4464+
In(Syntactic.trm_type, "SyntaxTerm",
4465+
In(Syntactic.delimiter_depth, "DelimiterDepth",
4466+
In(B.string, "ScopeName",
4467+
Out(Syntactic.trm_type, "ScopedSyntaxTerm",
4468+
Full(global, "Pushes the scope ScopeName on top of SyntaxTerm."))))),
4469+
fun t delim_depth scope _ ~depth coq_context _csts state ->
4470+
let open Syntactic in
4471+
let loc = to_coq_loc @@ State.get Rocq_elpi_builtins_synterp.invocation_site_loc state in
4472+
let Tag.{vl; _} = t in
4473+
let vl = CAst.make ~loc (Constrexpr.CDelimiters (delim_depth, scope, vl)) in
4474+
let ot = Tag.{t with vl} in
4475+
state, (!: ot), []
4476+
),
4477+
DocAbove);
4478+
MLCode(Pred("syntax.elaborate",
4479+
In(Syntactic.trm_type, "SyntaxTerm",
4480+
CIn(type_constraint, "TypeConstraint",
4481+
COut(term, "Term",
4482+
InOut(B.ioarg B.diagnostic, "Diagnostic",
4483+
Full(proof_context, "Elaborates SyntaxTerm using TypeConstraint. Respects @no-tc! and @no-coercion!"))))),
4484+
fun t expected_type _ diag ~depth coq_ctx csts state ->
4485+
let open Syntactic in
4486+
let Tag.{is;gs;vl} = t in
4487+
let vl = Ltac_plugin.Tacintern.intern_constr gs vl in
4488+
let sigma = get_sigma state in
4489+
let flags =
4490+
let open Pretyping in
4491+
let flags = all_no_fail_flags in
4492+
let options = coq_ctx.options in
4493+
let use_typeclasses = if Option.default false options.no_tc then NoUseTC else UseTC in
4494+
let use_coercions = not @@ Option.default false options.no_coercion in
4495+
{ flags with use_typeclasses; use_coercions }
4496+
in
4497+
try
4498+
let sigma, vl =
4499+
Ltac_plugin.Tacinterp.interp_open_constr ~flags ~expected_type is coq_ctx.env sigma vl
4500+
in
4501+
let state, extra_goals = set_current_sigma ~depth state sigma in
4502+
state, (!: vl +! B.mkOK), extra_goals
4503+
with e ->
4504+
diag_error_lazy diag @@ fun () ->
4505+
let error =
4506+
string_of_ppcmds coq_ctx.options @@
4507+
try CErrors.print_no_report e with | _ -> raise No_clause
4508+
in
4509+
state, ?: None +! B.mkERROR error, []
4510+
),
4511+
DocAbove);
4512+
]
4513+
43934514

43944515
;;

src/rocq_elpi_builtins_synterp.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ open Names
1919

2020
open Rocq_elpi_utils
2121
open Rocq_elpi_HOAS
22+
open Rocq_elpi_arg_HOAS
2223

2324
let prop = { B.any with Conv.ty = Conv.TyName "prop" }
2425

@@ -1229,7 +1230,7 @@ Supported attributes:
12291230
] @ SynterpAction.get_parsing_actions_synterp @ [
12301231
MLData attribute_value;
12311232
MLData attribute;
1232-
]
1233+
] @ Syntactic.ml_data
12331234

12341235
let synterp_api_doc = ". bla bla"
12351236

src/rocq_elpi_programs.ml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ type db = {
163163
units : Names.KNset.t;
164164
}
165165

166-
type nature = Command of { raw_args : bool } | Tactic | Program of { raw_args : bool }
166+
type nature = Command of { args : arg_kind } | Tactic | Program of { args : arg_kind }
167167

168168
type program = {
169169
sources_rev : qualified_name Code.t;
@@ -242,7 +242,7 @@ module type Programs = sig
242242
val db_inspect : qualified_name -> int
243243

244244
val get_and_compile_existing_db : loc:Loc.t -> qualified_name -> EC.program
245-
val get_and_compile : loc:Loc.t -> ?even_if_empty:bool -> qualified_name -> (EC.program * bool) option
245+
val get_and_compile : loc:Loc.t -> ?even_if_empty:bool -> qualified_name -> (EC.program * arg_kind) option
246246

247247
end
248248

@@ -804,7 +804,7 @@ let compile ~loc src =
804804
in
805805
compile_code src
806806

807-
let get_and_compile ~loc ?even_if_empty name : (EC.program * bool) option =
807+
let get_and_compile ~loc ?even_if_empty name : (EC.program * arg_kind) option =
808808
let t = Unix.gettimeofday () in
809809
let res = code ?even_if_empty name |> Option.map (fun src ->
810810
(* Format.eprintf "compile %a = %a" pp_qualified_name name (Code.pp Chunk.pp) src; *)
@@ -815,9 +815,9 @@ let get_and_compile ~loc ?even_if_empty name : (EC.program * bool) option =
815815
let prog = recache_code 0 old_hash new_hash prog src in
816816
let raw_args =
817817
match get_nature name with
818-
| Command { raw_args } -> raw_args
819-
| Program { raw_args } -> raw_args
820-
| Tactic -> true in
818+
| Command { args } -> args
819+
| Program { args } -> args
820+
| Tactic -> Elaborated in
821821
(prog, raw_args)) in
822822
Rocq_elpi_utils.elpitime (fun _ -> Pp.(str(Printf.sprintf "Elpi: get_and_compile %1.4f" (Unix.gettimeofday () -. t))));
823823
res

src/rocq_elpi_programs.mli

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ and src_db_header = {
2424
dast : cunit;
2525
}
2626

27-
type nature = Command of { raw_args : bool } | Tactic | Program of { raw_args : bool }
27+
type nature = Command of { args : arg_kind } | Tactic | Program of { args : arg_kind }
2828

2929

3030
module Chunk : sig
@@ -120,7 +120,7 @@ module type Programs = sig
120120
val db_inspect : qualified_name -> int
121121

122122
val get_and_compile_existing_db : loc:Loc.t -> qualified_name -> Compile.program
123-
val get_and_compile : loc:Loc.t -> ?even_if_empty:bool -> qualified_name -> (Compile.program * bool) option
123+
val get_and_compile : loc:Loc.t -> ?even_if_empty:bool -> qualified_name -> (Compile.program * arg_kind) option
124124

125125

126126
end

src/rocq_elpi_utils.mli

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ val gr2path: Names.GlobRef.t -> string list
9090

9191
val eta_contract : Environ.env -> Evd.evar_map -> EConstr.t -> EConstr.t
9292

93+
(* Command argument specifiers *)
94+
type arg_kind =
95+
| Elaborated
96+
| Unelaborated
97+
| Syntactic
98+
val arg_kind_merge : ?loc:Loc.t -> arg_kind option -> arg_kind -> arg_kind
99+
93100
(* Diagnostics *)
94101
val diag_error_lazy : ?on_ok:(unit -> 'a) -> (* defaults to [raise No_clause] *)
95102
Elpi.Builtin.diagnostic Elpi.API.BuiltInPredicate.ioarg ->

0 commit comments

Comments
 (0)