···11-include Signatures.JUDGMENT_VIEW with module Term := HOTerm and module Judgment := HOTerm
11+include Signatures.JUDGMENT_VIEW
22+ with module Term := HOTerm
33+ and module Judgment := TermAsJudgment.HOTermJ
+205-14
src/Method.res
···22open Util
33module Context = (Term: TERM, Judgment: JUDGMENT with module Term := Term) => {
44 module Rule = Rule.Make(Term, Judgment)
55- type rec t = {
55+ type t = {
66 fixes: array<Term.meta>,
77 facts: Dict.t<Rule.t>,
88 }
···1515 module Context: module type of Context(Term, Judgment)
1616 type t<'a>
1717 let keywords: array<string>
1818- let substitute: (t<'a>, Term.subst) => t<'a>
1818+ let substitute: (t<'a>, Judgment.subst) => t<'a>
1919 let check: (t<'a>, Context.t, Judgment.t, ('a, Rule.t) => 'b) => result<t<'b>, string>
2020- let apply: (Context.t, Judgment.t, Term.gen, Rule.t => 'a) => Dict.t<(t<'a>, Term.subst)>
2020+ let apply: (Context.t, Judgment.t, Term.gen, Rule.t => 'a) => Dict.t<(t<'a>, Judgment.subst)>
2121 let map: (t<'a>, 'a => 'b) => t<'b>
2222 let parse: (
2323 string,
···3939 module Context = Context(Term, Judgment)
4040 type t<'a> = {
4141 ruleName: string,
4242- instantiation: array<Term.t>,
4242+ instantiation: array<Judgment.substCodom>,
4343 subgoals: array<'a>,
4444 }
4545 let map = (it: t<'a>, f) => {
···4949 subgoals: it.subgoals->Array.map(f),
5050 }
5151 }
5252- let substitute = (it: t<'a>, subst: Term.subst) => {
5252+ let substitute = (it: t<'a>, subst: Judgment.subst) => {
5353 {
5454 ruleName: it.ruleName,
5555- instantiation: it.instantiation->Array.map(t => t->Term.substitute(subst)),
5555+ instantiation: it.instantiation->Array.map(t => t->Judgment.substituteSubstCodom(subst)),
5656 subgoals: it.subgoals,
5757 }
5858 }
···6464 ~indentation=0,
6565 ~subprinter: ('a, ~scope: array<Term.meta>, ~indentation: int=?) => string,
6666 ) => {
6767- let args = it.instantiation->Array.map(t => Term.prettyPrint(t, ~scope))
6767+ let args = it.instantiation->Array.map(t => Judgment.prettyPrintSubstCodom(t, ~scope))
6868 "by ("
6969- ->String.concat(Array.join([it.ruleName, ...args], " "))
6969+ ->String.concat(Array.join([it.ruleName]->Array.concat(args), " "))
7070 ->String.concat(") {")
7171 ->String.concat(
7272 if Array.length(it.subgoals) > 0 {
···9191 let instantiation = []
9292 let it = ref(Error(""))
9393 while {
9494- it := Term.parse(cur.contents, ~scope, ~gen)
9494+ it := Judgment.parseSubstCodom(cur.contents, ~scope, ~gen)
9595 it.contents->Result.isOk
9696 } {
9797 let (val, rest) = it.contents->Result.getExn
···138138 let apply = (ctx: Context.t, j: Judgment.t, gen: Term.gen, f: Rule.t => 'a) => {
139139 let ret = Dict.make()
140140 ctx.facts->Dict.forEachWithKey((rule, key) => {
141141- let insts =
142142- rule.vars->Array.map(m => Term.place(gen->Term.fresh(~replacing=m), ~scope=ctx.fixes))
141141+ let insts = rule->Rule.schematise(gen, ~scope=ctx.fixes)
143142 let res = rule->Rule.instantiate(insts)
144143 let substs = Judgment.unify(res.conclusion, j, ~gen)
145145- substs->Array.forEach(subst => {
144144+ substs->Seq.forEach(subst => {
146145 let new = {
147146 ruleName: key,
148147 instantiation: insts,
···184183 }
185184 }
186185 let updateAtKey = (it: t<'a>, key: int, f: 'a => 'a) => {
187187- let newsgs = [...it.subgoals]
186186+ let newsgs = it.subgoals->Array.copy
187187+ newsgs->Array.set(key, f(newsgs[key]->Option.getExn))
188188+ {...it, subgoals: newsgs}
189189+ }
190190+}
191191+192192+module Elimination = (Term: TERM, Judgment: JUDGMENT with module Term := Term) => {
193193+ module Rule = Rule.Make(Term, Judgment)
194194+ module Context = Context(Term, Judgment)
195195+ type t<'a> = {
196196+ ruleName: string,
197197+ elimName: string,
198198+ instantiation: array<Judgment.substCodom>,
199199+ subgoals: array<'a>,
200200+ }
201201+ exception InternalParseError(string)
202202+ let keywords = ["elim"]
203203+ let prettyPrint = (
204204+ it: t<'a>,
205205+ ~scope,
206206+ ~indentation=0,
207207+ ~subprinter: ('a, ~scope: array<Term.meta>, ~indentation: int=?) => string,
208208+ ) => {
209209+ let subgoalsSpacer = if Array.length(it.subgoals) > 0 {
210210+ newline
211211+ } else {
212212+ ""
213213+ }
214214+ let instantiation = Array.join(
215215+ it.instantiation->Array.map(t => Judgment.prettyPrintSubstCodom(t, ~scope)),
216216+ " ",
217217+ )
218218+ let subgoalsStr =
219219+ it.subgoals
220220+ ->Array.map(s => subprinter(s, ~scope, ~indentation=indentation + 2))
221221+ ->Array.join(newline)
222222+ `elim (${it.ruleName} ${it.elimName} ${instantiation}) {${subgoalsSpacer}${subgoalsStr}}`
223223+ }
224224+225225+ let map = (it: t<'a>, f) => {
226226+ {
227227+ ruleName: it.ruleName,
228228+ elimName: it.elimName,
229229+ instantiation: it.instantiation,
230230+ subgoals: it.subgoals->Array.map(f),
231231+ }
232232+ }
233233+234234+ let substitute = (it: t<'a>, subst: Judgment.subst) => {
235235+ {
236236+ ruleName: it.ruleName,
237237+ elimName: it.elimName,
238238+ instantiation: it.instantiation->Array.map(t => t->Judgment.substituteSubstCodom(subst)),
239239+ subgoals: it.subgoals,
240240+ }
241241+ }
242242+243243+ let parse = (input, ~keyword as _, ~scope, ~gen, ~subparser) => {
244244+ let cur = ref(String.trim(input))
245245+ if cur.contents->String.get(0) == Some("(") {
246246+ Rule.parseRuleName(cur.contents->String.sliceToEnd(~start=1))->Result.flatMap(((
247247+ ruleName,
248248+ rest,
249249+ )) => {
250250+ cur := rest
251251+ Rule.parseRuleName(cur.contents)->Result.flatMap(((elimName, rest)) => {
252252+ cur := rest
253253+ let instantiation = []
254254+ let it = ref(Error(""))
255255+ while {
256256+ it := Judgment.parseSubstCodom(cur.contents, ~scope, ~gen)
257257+ it.contents->Result.isOk
258258+ } {
259259+ let (val, rest) = it.contents->Result.getExn
260260+ Array.push(instantiation, val)
261261+ cur := String.trim(rest)
262262+ }
263263+ if cur.contents->String.get(0) == Some(")") {
264264+ cur := String.trim(cur.contents->String.sliceToEnd(~start=1))
265265+ let subgoals = []
266266+ if cur.contents->String.get(0) == Some("{") {
267267+ cur := String.trim(cur.contents->String.sliceToEnd(~start=1))
268268+ try {
269269+ while cur.contents->String.get(0) != Some("}") {
270270+ switch subparser(cur.contents, ~scope, ~gen) {
271271+ | Ok((sg, rest)) => {
272272+ Array.push(subgoals, sg)
273273+ cur := String.trim(rest)
274274+ }
275275+ | Error(e) => raise(InternalParseError(e))
276276+ }
277277+ }
278278+ if cur.contents->String.get(0) == Some("}") {
279279+ cur := String.trim(cur.contents->String.sliceToEnd(~start=1))
280280+ let res = {ruleName, elimName, instantiation, subgoals}
281281+ Console.log(("parsed elim", res))
282282+ Ok((res, cur.contents))
283283+ } else {
284284+ Error("} or subgoal proof expected")
285285+ }
286286+ } catch {
287287+ | InternalParseError(e) => Error(e)
288288+ }
289289+ } else {
290290+ Error("{ expected")
291291+ }
292292+ } else {
293293+ Error(") or term expected")
294294+ }
295295+ })
296296+ })
297297+ } else {
298298+ Error("Expected (")
299299+ }
300300+ }
301301+302302+ let check = (it: t<'a>, ctx: Context.t, j: Judgment.t, f: ('a, Rule.t) => 'b) => {
303303+ switch (ctx.facts->Dict.get(it.ruleName), ctx.facts->Dict.get(it.elimName)) {
304304+ | (None, _) => Error(`Cannot find rule '${it.ruleName}'`)
305305+ | (_, None) => Error(`Cannot find elimination fact '${it.elimName}'`)
306306+ | (Some(rule), Some(elim)) if rule.premises->Array.length > 0 => {
307307+ let {premises, conclusion} = Rule.instantiate(rule, it.instantiation)
308308+ let elimPremise = premises[0]->Option.getExn
309309+ let remainingPremises = premises->Array.sliceToEnd(~start=1)
310310+ if elimPremise.premises->Array.length > 0 {
311311+ Error(`Premise to eliminate in rule ${it.ruleName} has non-empty premises`)
312312+ } else if elim.premises->Array.length > 0 {
313313+ Error(`Elimination motive (?) ${it.elimName} has non-empty premises`)
314314+ } else if !Judgment.equivalent(elimPremise.conclusion, elim.conclusion) {
315315+ Error(`Premise to eliminate and elimination motive (?) ${it.elimName} do not match`)
316316+ } else if !Judgment.equivalent(conclusion, j) {
317317+ let concString = Judgment.prettyPrint(conclusion, ~scope=ctx.fixes)
318318+ let goalString = Judgment.prettyPrint(j, ~scope=ctx.fixes)
319319+ Error(`Conclusion of rule '${concString}' doesn't match goal '${goalString}'`)
320320+ } else if Array.length(it.subgoals) != Array.length(remainingPremises) {
321321+ let subgoalsRem = Array.length(it.subgoals)->Int.toString
322322+ let premsRem = Array.length(remainingPremises)->Int.toString
323323+ Error(
324324+ `Number of subgoals (${subgoalsRem}) doesn't match rule ${it.ruleName}'s remaining number (${premsRem})`,
325325+ )
326326+ } else {
327327+ Ok({
328328+ ruleName: it.ruleName,
329329+ elimName: it.elimName,
330330+ instantiation: it.instantiation,
331331+ subgoals: Belt.Array.zipBy(it.subgoals, remainingPremises, f),
332332+ })
333333+ }
334334+ }
335335+ | (Some(_), Some(_)) => Error(`Rule ${it.ruleName} doesn't have any premises`)
336336+ }
337337+ }
338338+339339+ let updateAtKey = (it: t<'a>, key: int, f: 'a => 'a) => {
340340+ let newsgs = it.subgoals->Array.copy
188341 newsgs->Array.set(key, f(newsgs[key]->Option.getExn))
189342 {...it, subgoals: newsgs}
190343 }
344344+345345+ let apply = (ctx: Context.t, j: Judgment.t, gen: Term.gen, f: Rule.t => 'a) => {
346346+ let ret = Dict.make()
347347+ let possibleRules =
348348+ ctx.facts
349349+ ->Dict.toArray
350350+ ->Array.filter(((_, r)) =>
351351+ r.premises->Array.length > 0 && (r.premises[0]->Option.getExn).premises->Array.length == 0
352352+ )
353353+ let possibleElims =
354354+ ctx.facts->Dict.toArray->Array.filter(((_, r)) => r.premises->Array.length == 0)
355355+ possibleRules->Array.forEach(((ruleName, rule)) => {
356356+ possibleElims->Array.forEach(((elimName, elim)) => {
357357+ let ruleInsts = rule->Rule.schematise(gen, ~scope=ctx.fixes)
358358+ let elimInsts = elim->Rule.schematise(gen, ~scope=ctx.fixes)
359359+ let (rule', elim) = (rule->Rule.instantiate(ruleInsts), elim->Rule.instantiate(elimInsts))
360360+ Judgment.unify((rule'.premises[0]->Option.getExn).conclusion, elim.conclusion)->Seq.forEach(
361361+ elimSub => {
362362+ let rule'' = rule'->Rule.substituteBare(elimSub)
363363+ Judgment.unify(rule''.conclusion, j, ~gen)->Seq.forEach(
364364+ ruleSub => {
365365+ let new = {
366366+ ruleName,
367367+ elimName,
368368+ instantiation: ruleInsts,
369369+ subgoals: rule.premises->Array.sliceToEnd(~start=1)->Array.map(f),
370370+ }
371371+ let subst = Judgment.mergeSubsts(elimSub, ruleSub)
372372+ ret->Dict.set(`elim ${ruleName} with ${elimName}`, (new, subst))
373373+ },
374374+ )
375375+ },
376376+ )
377377+ })
378378+ })
379379+ ret
380380+ }
191381}
382382+192383module Lemma = (Term: TERM, Judgment: JUDGMENT with module Term := Term) => {
193384 module Rule = Rule.Make(Term, Judgment)
194385 module Context = Context(Term, Judgment)
···204395 show: f(it.show),
205396 }
206397 }
207207- let substitute = (it: t<'a>, subst: Term.subst) => {
398398+ let substitute = (it: t<'a>, subst: Judgment.subst) => {
208399 {
209400 rule: it.rule->Rule.substitute(subst),
210401 proof: it.proof,
···11-include Signatures.JUDGMENT_VIEW with module Term := SExp and module Judgment := SExp
11+include Signatures.JUDGMENT_VIEW
22+ with module Term := SExp
33+ and module Judgment := TermAsJudgment.SExpJ
···11+type rec piece =
22+ | String(string)
33+ | Var({idx: int})
44+ | Schematic({schematic: int, allowed: array<int>})
55+type t = array<piece>
66+type subst = Map.t<int, t>
77+88+include Signatures.TERM
99+ with type t := t
1010+ and type meta = string
1111+ and type schematic = int
1212+ and type subst := subst
1313+1414+let fromSExp: SExp.t => t
1515+let toSExp: t => SExp.t
···11+include Signatures.TERM_VIEW with module Term := StringTerm
+14
src/TermAsJudgment.res
···11+open Signatures
22+33+module Make = (Term: TERM): (JUDGMENT with module Term := Term and type t = Term.t) => {
44+ include Term
55+ type substCodom = Term.t
66+ let prettyPrintSubstCodom = Term.prettyPrint
77+ let parseSubstCodom = Term.parse
88+ let placeSubstCodom = Term.place
99+ let upshiftSubstCodom = Term.upshift
1010+ let substituteSubstCodom = Term.substitute
1111+}
1212+1313+module SExpJ = Make(SExp)
1414+module HOTermJ = Make(HOTerm)
···11-import * as ComponentGraph from './componentgraph'
22-import { AxiomS, ConfS, TheoremS, InductiveS } from './Scratch.mjs'
33-import ReactDOM from 'react-dom/client';
44-import React from 'react';
55-66-11+import * as ComponentGraph from "./componentgraph";
22+import { AxiomS, ConfS, TheoremS, InductiveS, AxiomStr, TheoremStr } from "./Scratch.mjs";
33+import ReactDOM from "react-dom/client";
44+import React from "react";
7588-99-type Component = ComponentGraph.Component
66+type Component = ComponentGraph.Component;
107118//A bridge between any module that implements the COMPONENT signature from ReScript-land
129//and a ComponentGraph component.
···75727673window.localStorage.clear()
7774ComponentGraph.setup({
7878- "hol-comp": HolComp(AxiomS),
7575+ "hol-comp": HolComp(AxiomS),
7976 "hol-inductive": HolComp(InductiveS),
8080- "hol-config":HolComp(ConfS),
8181- "hol-proof": HolComp(TheoremS)
8282-});
7777+ "hol-config": HolComp(ConfS),
7878+ "hol-proof": HolComp(TheoremS),
7979+ "hol-string": HolComp(AxiomStr),
8080+ "hol-string-proof": HolComp(TheoremStr),
8181+}); //"hol-config": ConfigComponent, "hol-proof":ProofComponent});
+11-11
tests/HOTermTest.res
···136136 let x = "x"
137137 let y = "y"
138138 t->testUnify(x, x)
139139- t->Util.testNotUnify(y, x)
140140- t->Util.testNotUnify(x, y)
139139+ t->Util.testUnifyFail(y, x)
140140+ t->Util.testUnifyFail(x, y)
141141 })
142142 t->block("applications", t => {
143143 let ab = "(a b)"
144144 let cd = "(c d)"
145145 t->testUnify(ab, ab)
146146 t->testUnify(cd, cd)
147147- t->Util.testNotUnify(ab, cd)
148148- t->Util.testNotUnify(cd, ab)
147147+ t->Util.testUnifyFail(ab, cd)
148148+ t->Util.testUnifyFail(cd, ab)
149149 })
150150 t->block("flex-rigid", t => {
151151 let x = "?0"
···175175 t->block("flex-rigid", t => {
176176 let x = "(?0 \\10)"
177177 let y = "(r (fst \\10))"
178178- t->Util.testUnify(
178178+ t->Util.testUnify1(
179179 x,
180180 y,
181181 ~subst=emptySubst->substAdd(
···193193 t->block("flex-rigid-fcu-2", t => {
194194 let x = "(?0 (fst \\10))"
195195 let y = "(r (fst (fst \\10)))"
196196- t->Util.testUnify(
196196+ t->Util.testUnify1(
197197 x,
198198 y,
199199 ~subst=emptySubst->substAdd(
···205205 t->block("flex-rigid-fcu-3", t => {
206206 let x = "(?1 (fst \\10) (snd \\1))"
207207 let y = "(r (q (snd \\1) (fst \\10)))"
208208- t->Util.testUnify(
208208+ t->Util.testUnify1(
209209 x,
210210 y,
211211 ~subst=emptySubst->substAdd(
···217217 t->block("flex-rigid-fcu-4", t => {
218218 let x = "(?1 (fst \\10) \\1)"
219219 let y = "(r (q (snd \\1) (fst \\10)))"
220220- t->Util.testUnify(
220220+ t->Util.testUnify1(
221221 x,
222222 y,
223223 ~subst=emptySubst->substAdd(
···252252 let a = "(x. ?0 x)"
253253 let b = "(x. f (?0 x))"
254254 // ?0 occurs in the rigid term on the right → should not unify
255255- t->Util.testNotUnify(a, b)
255255+ t->Util.testUnifyFail(a, b)
256256 })
257257 t->block("no capture", t => {
258258 let a = "(x. ?0)"
259259 let b = "(x. x)"
260260 // Should fail: it cannot capture the bound variable.
261261- t->Util.testNotUnify(a, b)
261261+ t->Util.testUnifyFail(a, b)
262262 })
263263 t->block("eta", t => {
264264 t->testUnify(
···309309 let a = "(Nat (S ?6))"
310310 let b = "(Nat (S (S \\0)))"
311311 let c = "(Nat (S (?6 \\0)))"
312312- t->Util.testNotUnify(a, b)
312312+ t->Util.testUnifyFail(a, b)
313313 t->testUnify(c, b, ~subst=emptySubst->substAdd(6, t->Util.parse("(x. S \\0)")))
314314 })
315315 t->block("tests from induction examples", t => {