the next generation of the in-browser educational proof assistant
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #21 from mio-19/main

constructor proof methods

authored by

Liam O'Connor and committed by
GitHub
25c461dd f00a5280

+294 -2
+15
index.html
··· 418 418 419 419 |- ? 420 420 </hol-proof> 421 + <hol-proof id="index.html/constructor-neq-test" deps="index.html/myconfig index.html/baz index.html/nat"> 422 + x. y. 423 + -------------------- neq-constructors 424 + (not (= (@Z x) (@S y))) 425 + 426 + x. y. |- ? 427 + </hol-proof> 428 + <hol-proof id="index.html/constructor-inj-test" deps="index.html/myconfig index.html/baz index.html/nat"> 429 + x. y. 430 + (= (@S x) (@S y)) 431 + -------------- S-inj 432 + (= x y) 433 + 434 + x. y. eq |- ? 435 + </hol-proof> 421 436 <hol-proof id="index.html/fcu-test" deps="index.html/myconfig"> 422 437 x. 423 438 [P. (P (fst x))]
+1 -1
package.json
··· 10 10 "preview": "vite preview", 11 11 "res:build": "rescript", 12 12 "res:clean": "rescript clean", 13 - "res:dev": "rescript -w", 13 + "res:dev": "rescript watch", 14 14 "res:format": "rescript format", 15 15 "test": "npm run res:build && pta 'tests/*.mjs'", 16 16 "test-watch": "onchange --initial '{tests,src}/*.mjs' -- pta 'tests/*.mjs'",
+209
src/Method.res
··· 763 763 let reversed = true 764 764 }, 765 765 ) 766 + 767 + module ConstructorNeq = ( 768 + Judgment: JUDGMENT with module Term := HOTerm and type subst = HOTerm.subst and type t = HOTerm.t, 769 + ) => { 770 + module Term = HOTerm 771 + module Rule = Rule.Make(HOTerm, Judgment) 772 + module Context = Context(HOTerm, Judgment) 773 + 774 + type t<'a> = unit 775 + 776 + type constructorHead = {name: string, args: array<HOTerm.t>} 777 + 778 + let constructorHead = (term: HOTerm.t): option<constructorHead> => { 779 + let (head, args) = HOTerm.strip(term) 780 + switch head { 781 + | HOTerm.Symbol({name, constructor: true}) => Some({name, args}) 782 + | _ => None 783 + } 784 + } 785 + 786 + type constructorComparison = {lhs: constructorHead, rhs: constructorHead, negated: bool} 787 + 788 + let extractConstructorEqualityFrom = (term: HOTerm.t): option<( 789 + constructorHead, 790 + constructorHead, 791 + )> => { 792 + let (head, args) = HOTerm.strip(term) 793 + switch head { 794 + | HOTerm.Symbol({name: "="}) if Array.length(args) == 2 => 795 + switch ( 796 + constructorHead(args->Array.getUnsafe(0)), 797 + constructorHead(args->Array.getUnsafe(1)), 798 + ) { 799 + | (Some(lhs), Some(rhs)) => Some((lhs, rhs)) 800 + | _ => None 801 + } 802 + | _ => None 803 + } 804 + } 805 + 806 + let extractConstructorEquality = (judgment: Judgment.t): option<constructorComparison> => { 807 + let reduced = judgment->Judgment.reduce 808 + switch extractConstructorEqualityFrom(reduced) { 809 + | Some((lhs, rhs)) => Some({lhs, rhs, negated: false}) 810 + | None => 811 + switch HOTerm.strip(reduced) { 812 + | (HOTerm.Symbol({name: "not"}), [inner]) => 813 + extractConstructorEqualityFrom(inner)->Option.map(((lhs, rhs)) => {lhs, rhs, negated: true}) 814 + | _ => None 815 + } 816 + } 817 + } 818 + 819 + let keywords = ["constructor_neq"] 820 + 821 + let map = (it: t<'a>, _f: 'a => 'b): t<'b> => it 822 + 823 + let substitute = (it: t<'a>, _subst: Judgment.subst) => it 824 + 825 + let prettyPrint = (_it: t<'a>, ~scope as _, ~indentation=0, ~subprinter as _) => 826 + String.repeat(" ", indentation)->String.concat("constructor_neq") 827 + 828 + let parse = (input, ~keyword as _, ~scope as _, ~gen as _, ~subparser as _) => Ok(( 829 + (), 830 + String.trim(input), 831 + )) 832 + 833 + let apply = (_ctx: Context.t, j: Judgment.t, _gen: HOTerm.gen, _f: Rule.t => 'a) => { 834 + let ret = Dict.make() 835 + switch extractConstructorEquality(j) { 836 + | Some({lhs: {name: lhs}, rhs: {name: rhs}}) if lhs != rhs => 837 + ret->Dict.set(`constructor_neq ${lhs} ${rhs}`, ((), HOTerm.makeSubst())) 838 + | _ => () 839 + } 840 + ret 841 + } 842 + 843 + let check = (_it: t<'a>, _ctx: Context.t, goal: Judgment.t, _f: ('a, Rule.t) => 'b) => 844 + switch extractConstructorEquality(goal) { 845 + | Some({lhs: {name: lhs}, rhs: {name: rhs}}) => 846 + if lhs == rhs { 847 + Error("constructor_neq expects different constructor symbols") 848 + } else { 849 + Ok() 850 + } 851 + | None => Error("constructor_neq applies only to equalities between constructors") 852 + } 853 + } 854 + 855 + module ConstructorInj = ( 856 + Judgment: JUDGMENT with module Term := HOTerm and type subst = HOTerm.subst and type t = HOTerm.t, 857 + ) => { 858 + module Rule = Rule.Make(HOTerm, Judgment) 859 + module Context = Context(HOTerm, Judgment) 860 + 861 + // we need to define this to workaround a type error for map 862 + type inner = { 863 + source: string, 864 + argIndex: int, 865 + } 866 + type t<'a> = inner 867 + 868 + let keywords = ["constructor_inj"] 869 + 870 + let map = (it: t<'a>, _f: 'a => 'b): t<'b> => it 871 + 872 + let substitute = (it: t<'a>, _subst: Judgment.subst) => it 873 + 874 + let prettyPrint = (it: t<'a>, ~scope as _, ~indentation=0, ~subprinter as _) => 875 + String.repeat(" ", indentation)->String.concat( 876 + `constructor_inj ${it.source} ${Int.toString(it.argIndex)}`, 877 + ) 878 + 879 + let parseIntPrefix = (input: string): option<(int, string)> => { 880 + let cur = input->String.trimStart 881 + let re = RegExp.fromStringWithFlags("^(-?[0-9]+)", ~flags="y") 882 + switch re->RegExp.exec(cur) { 883 + | None => None 884 + | Some(res) => 885 + switch RegExp.Result.matches(res) { 886 + | [n] => 887 + Some(( 888 + n->Int.fromString->Option.getExn, 889 + cur->String.sliceToEnd(~start=RegExp.lastIndex(re)), 890 + )) 891 + | _ => None 892 + } 893 + } 894 + } 895 + 896 + let parse = (input, ~keyword as _, ~scope as _, ~gen as _, ~subparser as _) => { 897 + let cur = String.trim(input) 898 + switch Rule.parseRuleName(cur) { 899 + | Error(e) => Error(e) 900 + | Ok((source, rest)) => 901 + let rest = String.trim(rest) 902 + switch parseIntPrefix(rest) { 903 + | Some((argIndex, remainder)) => Ok(({source, argIndex}, remainder->String.trim)) 904 + | None => Ok(({source, argIndex: 0}, rest)) 905 + } 906 + } 907 + } 908 + 909 + let extractConstructorEquality = (term: HOTerm.t): option<( 910 + string, 911 + array<HOTerm.t>, 912 + array<HOTerm.t>, 913 + )> => { 914 + let (_head, args) = term->HOTerm.strip 915 + if Array.length(args) != 2 { 916 + None 917 + } else { 918 + let (lHead, lArgs) = args->Array.getUnsafe(0)->HOTerm.strip 919 + let (rHead, rArgs) = args->Array.getUnsafe(1)->HOTerm.strip 920 + switch (lHead, rHead) { 921 + | (HOTerm.Symbol({name: ln, constructor: true}), HOTerm.Symbol({name: rn, constructor: true})) 922 + if ln == rn && lArgs->Array.length == rArgs->Array.length => 923 + Some((ln, lArgs, rArgs)) 924 + | _ => None 925 + } 926 + } 927 + } 928 + 929 + let apply = (ctx: Context.t, j: Judgment.t, _gen: HOTerm.gen, _f: Rule.t => 'a) => { 930 + let ret = Dict.make() 931 + switch j->Judgment.reduce->HOTerm.strip { 932 + | (HOTerm.Symbol({name: "="}), [lhs, rhs]) => 933 + ctx.facts->Dict.forEachWithKey((fact, name) => { 934 + switch extractConstructorEquality(fact.conclusion) { 935 + | Some((_cName, lArgs, rArgs)) => 936 + Belt.Array.zip(lArgs, rArgs)->Array.forEachWithIndex(((la, ra), idx) => 937 + if HOTerm.equivalent(la, lhs) && HOTerm.equivalent(ra, rhs) { 938 + ret->Dict.set( 939 + `constructor_inj ${name} ${Int.toString(idx)}`, 940 + ({source: name, argIndex: idx}, HOTerm.makeSubst()), 941 + ) 942 + } 943 + ) 944 + | None => () 945 + } 946 + }) 947 + | _ => () 948 + } 949 + ret 950 + } 951 + 952 + let check = (it: t<'a>, ctx: Context.t, goal: Judgment.t, _f: ('a, Rule.t) => 'b) => { 953 + switch (ctx.facts->Dict.get(it.source), goal->Judgment.reduce->HOTerm.strip) { 954 + | (None, _) => Error(`Cannot find equality '${it.source}'`) 955 + | (Some(fact), (HOTerm.Symbol({name: "="}), [lhs, rhs])) => 956 + switch extractConstructorEquality(fact.conclusion) { 957 + | None => Error(`'${it.source}' is not a constructor equality`) 958 + | Some((_cName, lArgs, rArgs)) => 959 + if it.argIndex < 0 || it.argIndex >= Array.length(lArgs) { 960 + Error("constructor_inj index out of range") 961 + } else { 962 + let la = lArgs->Array.getUnsafe(it.argIndex) 963 + let ra = rArgs->Array.getUnsafe(it.argIndex) 964 + if HOTerm.equivalent(la, lhs) && HOTerm.equivalent(ra, rhs) { 965 + Ok({source: it.source, argIndex: it.argIndex}) 966 + } else { 967 + Error("constructor_inj target does not match goal") 968 + } 969 + } 970 + } 971 + | (_, _) => Error("constructor_inj applies to equality goals") 972 + } 973 + } 974 + }
+56
src/MethodView.res
··· 245 245 } 246 246 } 247 247 248 + module ConstructorNeqView = ( 249 + Judgment: JUDGMENT with module Term := HOTerm and type subst = HOTerm.subst and type t = HOTerm.t, 250 + ) => { 251 + module Method = Method.ConstructorNeq(Judgment) 252 + type props<'a> = { 253 + method: Method.t<'a>, 254 + scope: array<HOTerm.meta>, 255 + ruleStyle: RuleView.style, 256 + gen: HOTerm.gen, 257 + onChange: (Method.t<'a>, Judgment.subst) => unit, 258 + } 259 + type srProps<'a> = { 260 + "proof": 'a, 261 + "scope": array<HOTerm.meta>, 262 + "ruleStyle": RuleView.style, 263 + "gen": HOTerm.gen, 264 + "onChange": ('a, Judgment.subst) => unit, 265 + } 266 + let make = (_subRender: srProps<'a> => React.element) => 267 + _props => { 268 + <div> 269 + <b> {React.string("constructor_neq")} </b> 270 + </div> 271 + } 272 + } 273 + 274 + module ConstructorInjView = ( 275 + Judgment: JUDGMENT with module Term := HOTerm and type subst = HOTerm.subst and type t = HOTerm.t, 276 + ) => { 277 + module Method = Method.ConstructorInj(Judgment) 278 + type props<'a> = { 279 + method: Method.t<'a>, 280 + scope: array<HOTerm.meta>, 281 + ruleStyle: RuleView.style, 282 + gen: HOTerm.gen, 283 + onChange: (Method.t<'a>, Judgment.subst) => unit, 284 + } 285 + type srProps<'a> = { 286 + "proof": 'a, 287 + "scope": array<HOTerm.meta>, 288 + "ruleStyle": RuleView.style, 289 + "gen": HOTerm.gen, 290 + "onChange": ('a, Judgment.subst) => unit, 291 + } 292 + let make = (_subRender: srProps<'a> => React.element) => 293 + props => { 294 + <div> 295 + <b> 296 + {React.string( 297 + `constructor_inj ${props.method.source} ${Int.toString(props.method.argIndex)}`, 298 + )} 299 + </b> 300 + </div> 301 + } 302 + } 303 + 248 304 module CombineMethodView = ( 249 305 Term: TERM, 250 306 Judgment: JUDGMENT with module Term := Term,
+13 -1
src/Scratch.res
··· 3 3 module AxiomS = Editable.TextArea(AxiomSet.Make(HOTerm, HOTermJ, HOTermJView)) 4 4 module InductiveS = Editable.TextArea(InductiveSet.Make(HOTerm, HOTermJ, HOTermJView)) 5 5 6 - module RewritesView = MethodView.CombineMethodView( 6 + module EqualityViews = MethodView.CombineMethodView( 7 7 HOTerm, 8 8 HOTermJ, 9 9 MethodView.RewriteView(HOTermJ), 10 10 MethodView.RewriteReverseView(HOTermJ), 11 + ) 12 + module ConstructorEqualityViews = MethodView.CombineMethodView( 13 + HOTerm, 14 + HOTermJ, 15 + EqualityViews, 16 + MethodView.ConstructorNeqView(HOTermJ), 17 + ) 18 + module RewritesView = MethodView.CombineMethodView( 19 + HOTerm, 20 + HOTermJ, 21 + ConstructorEqualityViews, 22 + MethodView.ConstructorInjView(HOTermJ), 11 23 ) 12 24 module DerivationsOrLemmasView = MethodView.CombineMethodView( 13 25 HOTerm,