···11+22+type rec t =
33+ Symbol({name: string })
44+ | Compound({subexps: array<t>})
55+ | Var({idx:int})
66+ | Schematic({schematic:int, allowed: array<int>})
77+88+include Signatures.TERM with
99+ type t := t and
1010+ type meta = string and
1111+ type schematic = int and
1212+ type subst = Map.t<int,t>
···11+22+module type TERM = {
33+ type t
44+ type schematic
55+ type meta
66+ type subst = Map.t<schematic,t>
77+ let substitute : (t, subst) => t
88+ let unify : (t, t) => array<subst>
99+ let substDeBruijn : (t, array<t>, ~from:int=?) => t
1010+ let upshift : (t, int, ~from:int=?) => t
1111+ type gen
1212+ let fresh : (gen, ~replacing:meta=?) => schematic
1313+ let seen : (gen, schematic) => ()
1414+ let place : (schematic, ~scope: array<meta>) => t
1515+ let makeGen : () => gen
1616+ let parse : (string, ~scope: array<meta>, ~gen: gen=?) => result<(t,string),string>
1717+ let parseMeta : (string) => result<(meta,string),string>
1818+ let prettyPrint : (t, ~scope: array<meta>) => string
1919+ let prettyPrintMeta : (meta) => string
2020+}
2121+2222+module type JUDGMENT = {
2323+ module Term : TERM
2424+ type t
2525+ let substitute : (t, Term.subst) => t
2626+ let unify : (t, t) => array<Term.subst>
2727+ let substDeBruijn : (t, array<Term.t>, ~from:int=?) => t
2828+ let upshift : (t, int, ~from:int=?) => t
2929+ let parse : (string, ~scope: array<Term.meta>, ~gen: Term.gen=?) => result<(t,string),string>
3030+ let prettyPrint : (t, ~scope: array<Term.meta>) => string
3131+}
3232+3333+3434+module type TERM_VIEW = {
3535+ module Term : TERM
3636+ type props = {term: Term.t, scope: array<Term.meta>}
3737+ let make : props => React.element
3838+ let makeMeta : Term.meta => React.element
3939+}
4040+4141+module type JUDGMENT_VIEW = {
4242+ module Term : TERM
4343+ module Judgment : JUDGMENT with module Term := Term;
4444+ module TermView : TERM_VIEW with module Term := Term;
4545+ type props = {judgment: Judgment.t, scope: array<Term.meta>}
4646+ let make : props => React.element
4747+}
4848+4949+
-459
src/Test.res
···11-open Demo
22-module type TERM_VIEW = {
33- module Term : TERM
44- type props = {term: Term.t, scope: array<Term.meta>}
55- let make : props => React.element
66- let makeMeta : Term.meta => React.element
77-}
88-99-module type JUDGMENT_VIEW = {
1010- module Base : BASE
1111- open Base
1212- module TermView : TERM_VIEW with module Term := Term;
1313- type props = {judgment: Judgment.t, scope: array<Term.meta>}
1414- let make : props => React.element
1515-}
1616-1717-module SExpView : TERM_VIEW with module Term := SExp = {
1818-1919- type props = {term: SExp.t, scope: array<string>}
2020- let viewVar = (idx, scope:array<string>) => {
2121- switch scope[idx] {
2222- | Some(n) if Array.indexOf(scope,n) == idx => <span className="term-metavar"> { React.string(n) } </span>
2323- | _ => <span className="term-metavar-unnamed"> {React.string("\\")} { React.int(idx) } </span>
2424- }
2525- }
2626- let makeMeta = (str : string) => {
2727- <span className="rule-binder">{React.string(str)}{React.string(".")}</span>
2828- }
2929- let parenthesise = (f) =>
3030- [<span className="symbol">{React.string("(")}</span>,...f,<span className="symbol">{React.string(")")}</span>]
3131- let intersperse = (a) => a->Array.flatMapWithIndex((e, i) =>
3232- if i == 0 { [e] } else { [React.string(" "),e] })
3333- @react.componentWithProps
3434- let rec make = ({term, scope}) => switch term {
3535- | Compound({subexps:bits}) => {
3636- <span className="term-compound">
3737- {bits
3838- ->Array.map(t => make({term:t,scope}))
3939- ->intersperse->parenthesise->React.array}
4040- </span>
4141- }
4242- | Var({idx:idx}) => viewVar(idx,scope)
4343- | Symbol({name:s}) => <span className="term-const"> { React.string(s) } </span>
4444- | Schematic({schematic:s, allowed:vs}) =>
4545- <span className="term-schematic">
4646- {React.string("?")} {React.int(s)}
4747- <span className="term-schematic-telescope">
4848- {vs
4949- ->Array.map(v => viewVar(v,scope))
5050- ->intersperse->parenthesise->React.array}
5151- </span>
5252- </span>
5353- }
5454-}
5555-module SExpBase = {module Term = SExp; module Judgment = SExp}
5656-module SExpJView : JUDGMENT_VIEW with module Base := SExpBase = {
5757- module TermView = SExpView
5858- type props = {judgment: SExp.t, scope: array<string>}
5959- let make = (props : props) => SExpView.make({term: props.judgment,scope:props.scope})
6060-}
6161-6262-module RuleView = (Base : BASE,
6363- TermView : TERM_VIEW with module Term := Base.Term,
6464- JudgmentView : JUDGMENT_VIEW with module Base := Base) => {
6565- module PE = ProofEngine(Base)
6666- open PE
6767- type style = Gentzen | Linear | Hybrid
6868-6969- type props = {rule:Rule.t, style: style, scope?: array<Base.Term.meta>, children: React.element}
7070- module type RULE_COMPONENT = {
7171- let make : props => React.element
7272- }
7373- module Inline : RULE_COMPONENT = {
7474- let rec make = (props : props) => {
7575- let {vars, premises,conclusion} = props.rule
7676- let scope = vars->Array.concat(props.scope->Option.getOr([]))
7777- let arr = vars->Array.map(TermView.makeMeta)
7878- Array.reverse(arr)
7979- <span className="inline-rule">
8080- <span className="rule-rulename-defined">
8181- {props.children}
8282- </span>
8383- {if Array.length(arr) > 0 { <span className="rule-binders">{React.array(arr)}</span> }
8484- else { React.string("") }}
8585- {React.array(premises
8686- ->Array.map(p=><span className="rule-context">
8787- {make({rule:p, scope,children:React.string(""),style:props.style})}</span>)
8888- ->Array.flatMapWithIndex( (e, i) =>
8989- if i == 0 {
9090- [e]
9191- } else {
9292- [<span className="symbol symbol-bold symbol-comma">{React.string(",")}</span>,e]
9393- }))}
9494- {if premises->Array.length > 0 {
9595- <span className="symbol symbol-turnstile symbol-bold">
9696- {React.string("⊢")}
9797- </span>
9898- } else {
9999- React.string("")
100100- }}
101101- <JudgmentView judgment={conclusion}
102102- scope={scope} />
103103- </span>
104104- }
105105- }
106106- module Hypothetical = (Premise : RULE_COMPONENT) => {
107107- let make = (props : props) => if Array.length(props.rule.premises) == 0 { Inline.make(props) } else {
108108- let {vars, premises,conclusion} = props.rule
109109- let arr = vars->Array.map(TermView.makeMeta)
110110- Array.reverse(arr)
111111- let scope = vars->Array.concat(props.scope->Option.getOr([]))
112112- <table className="inference">
113113- <tr><td className="rule-cell rule-binderbox" rowSpan=3>{React.array(arr)}</td>
114114- {React.array(premises->Array.map(p=>
115115- <td className="rule-cell rule-premise">
116116- <Premise rule={p} scope={scope} style={props.style}>
117117- {React.string("")}
118118- </Premise>
119119- </td>))}
120120- <td className="rule-cell rule-spacer"></td>
121121- <td rowSpan=3 className="rule-cell rule-rulebox">
122122- <span className="rule-rulename-defined">
123123- {props.children}
124124- </span>
125125- </td>
126126- </tr>
127127- <tr> <td colSpan={premises->Array.length + 1} className="rule-cell">
128128- {React.string("⋮")}
129129- </td></tr>
130130- <tr>
131131- <td colSpan={premises->Array.length + 1} className="rule-cell rule-hypothetical-conclusion">
132132- <JudgmentView judgment={conclusion} scope={scope} />
133133- </td>
134134- </tr></table>
135135- }
136136- }
137137- module TopLevel = (Premise : RULE_COMPONENT) => {
138138- let make = (props : props) => {
139139- let {vars, premises,conclusion} = props.rule
140140- let arr = vars->Array.map(TermView.makeMeta)
141141- Array.reverse(arr)
142142- let scope = vars->Array.concat(props.scope->Option.getOr([]))
143143- <div className="axiom"><table className="inference">
144144- <tr><td className="rule-cell rule-binderbox" rowSpan=2>{React.array(arr)}</td>
145145- {React.array(premises->Array.map(p=>
146146- <td className="rule-cell rule-premise">
147147- <Premise rule={p} scope={scope} style={props.style}>
148148- {React.string("")}
149149- </Premise>
150150- </td>))}
151151- <td className="rule-cell rule-spacer"></td>
152152- <td rowSpan=2 className="rule-cell rule-rulebox">
153153- <span className="rule-rulename-defined">
154154- {props.children}
155155- </span>
156156- </td>
157157- </tr>
158158- <tr>
159159- <td colSpan={premises->Array.length + 1} className="rule-cell rule-conclusion">
160160- <JudgmentView judgment={conclusion} scope={scope} />
161161- </td>
162162- </tr></table></div>
163163- }
164164- }
165165- module Gentzen = TopLevel(Hypothetical(Inline))
166166- module Hybrid = TopLevel(Inline)
167167-168168- @react.componentWithProps
169169- let make = (props) => {
170170- switch (props.style) {
171171- | Hybrid => Hybrid.make(props)
172172- | Gentzen => Gentzen.make(props)
173173- | Linear => Inline.make(props)
174174- }
175175- }
176176-}
177177-178178-179179-180180-module type TEXTUAL_COMPONENT = {
181181- type props
182182- type state
183183- let getState : props => state
184184- let setState : (props,state) => props
185185- let serialise : (props, state) => string
186186- let deserialise : (props, string) => result<state,string>
187187- let onChange : props => ()
188188- let make : props => React.element
189189-}
190190-191191-module TermTextualComponent =
192192- (Term : TERM, TermView : TERM_VIEW with module Term := Term ) => {
193193- type state = Term.t
194194- type rec props = {
195195- term: Term.t,
196196- scope: array<Term.meta>,
197197- gen?: Term.gen,
198198- onChange: props => ()
199199- }
200200- let getState = (props) => props.term
201201- let setState = (props,state) => {...props,term:state}
202202-203203- let serialise = (props : props,state) => {
204204- state->Term.prettyPrint(~scope=props.scope)
205205- }
206206- let onChange = (props) => props.onChange(props)
207207- let deserialise = (props : props, str : string) => {
208208- switch Term.parse(str,~scope=props.scope,~gen=?props.gen) {
209209- | Ok(t,"") => { Ok(t) }
210210- | Ok(_,_) => Error("additional input beyond expression")
211211- | Error(e) => Error(e)
212212- }
213213- }
214214- let make = ({term,scope}) => {
215215- <TermView term=term scope=scope/>
216216- }
217217-}
218218-module JudgmentTextualComponent =
219219- (Base : BASE, JudgmentView : JUDGMENT_VIEW with module Base := Base ) => {
220220- open Base
221221- type rec props = {
222222- judgment: Judgment.t,
223223- scope: array<Term.meta>,
224224- gen?: Term.gen,
225225- onChange: props => ()
226226- }
227227- type state = Judgment.t
228228- let getState = (props) => props.judgment
229229- let setState = (props,judgment) => {...props,judgment}
230230- let onChange = (props) => props.onChange(props)
231231- let serialise = (props : props, state:state) => {
232232- state->Judgment.prettyPrint(~scope=props.scope)
233233- }
234234- let deserialise = (props : props, str : string) => {
235235- switch Judgment.parse(str,~scope=props.scope,~gen=?props.gen) {
236236- | Ok(t,"") => Ok(t)
237237- | Ok(_,_) => Error("additional input beyond expression")
238238- | Error(e) => Error(e)
239239- }
240240- }
241241- let make = ({judgment,scope}) => {
242242- <JudgmentView judgment=judgment scope=scope/>
243243- }
244244-}
245245-module RuleTextualComponent = (
246246- Base : BASE,
247247- TermView : TERM_VIEW with module Term := Base.Term,
248248- JudgmentView : JUDGMENT_VIEW with module Base := Base
249249-) => {
250250- module PE = ProofEngine(Base)
251251- open PE
252252- open Base
253253- module TheRuleView = RuleView(Base,TermView,JudgmentView)
254254- type rec props = {
255255- rule: Rule.t,
256256- scope: array<Term.meta>,
257257- name?: string,
258258- gen?: Term.gen,
259259- style: TheRuleView.style,
260260- onChange: props => ()
261261- }
262262- type state = (Rule.t, string)
263263- let getState = (props) => (props.rule,props.name->Option.getOr(""))
264264- let setState = (props,(rule,name)) => {...props,rule,name}
265265- let onChange = (props) => props.onChange(props)
266266- let serialise = (props : props, state) => {
267267- state->Rule.prettyPrintTopLevel(~scope=props.scope,~name=?props.name)
268268- }
269269- let deserialise = (props : props, str : string) => {
270270- switch Rule.parseTopLevel(str,~scope=props.scope,~gen=?props.gen) {
271271- | Ok(r,"") => Ok(r)
272272- | Ok(_,_) => Error("additional input beyond expression")
273273- | Error(e) => Error(e)
274274- }
275275- }
276276- let make = (props) => {
277277- <TheRuleView rule={props.rule} scope={props.scope}
278278- style={props.style}>
279279- {React.string(props.name->Option.getOr(""))}
280280- </TheRuleView>
281281- }
282282-}
283283-284284-module RuleSetTextualComponent = (
285285- Base : BASE,
286286- TermView : TERM_VIEW with module Term := Base.Term,
287287- JudgmentView : JUDGMENT_VIEW with module Base := Base
288288-) => {
289289- module PE = ProofEngine(Base)
290290- open PE
291291- open Base
292292- module TheRuleView = RuleView(Base,TermView,JudgmentView)
293293- type state = Dict.t<Rule.t>
294294- type rec props = {
295295- rules: Dict.t<Rule.t>,
296296- style: TheRuleView.style,
297297- onChange: props => ()
298298- }
299299- let getState = (props) => props.rules
300300- let setState = (props,state) => {...props,rules:state}
301301- let onChange = (props) => props.onChange(props)
302302- let serialise = (props : props, state) => {
303303- let results = [];
304304- state->Dict.forEachWithKey((value, key) => {
305305- results->Array.push(value->Rule.prettyPrintTopLevel(~scope=[],~name=key))
306306- })
307307- results->Array.join(Demo.newline)
308308- }
309309- let deserialise = (props : props, str : string) => {
310310- let cur = ref(str)
311311- let go = ref(true)
312312- let results = Dict.make()
313313- let ret = ref(Error("impossible"))
314314- while go.contents {
315315- switch Rule.parseTopLevel(cur.contents,~scope=[]) {
316316- | Ok((t,n),rest) => {
317317- if n->String.trim == "" {
318318- go := false
319319- ret := Error("Rule given with no name")
320320- } else {
321321- Dict.set(results,n,t)
322322- if rest->String.trim == "" {
323323- go := false
324324- ret := Ok(results)
325325- } else {
326326- cur := rest
327327- }
328328- }
329329- }
330330- | Error(e) => { go := false; ret := Error(e) }
331331- }
332332- }
333333- ret.contents
334334- }
335335- let make = (props) => {
336336- <div className={"axiom-set axiom-set-"->String.concat(String.make(props.style))}>
337337- { Dict.toArray(props.rules)->Array.map(((n,r)) =>
338338- <TheRuleView rule={r} scope={[]} style={props.style}>
339339- {React.string(n)}
340340- </TheRuleView>)
341341- ->React.array
342342- }
343343- </div>
344344- }
345345-}
346346-347347-module Editable = (Underlying : TEXTUAL_COMPONENT) => {
348348- type props = Underlying.props
349349- @react.componentWithProps
350350- let make = props => {
351351-352352- let (editing,setEditing) = React.useState (_ => "off")
353353- let (tree,setTree) = React.useState (_=>Underlying.getState(props))
354354- let (text,setText) =
355355- React.useState (_=> Underlying.serialise(props,tree))
356356- let done = _ => {
357357- switch Underlying.deserialise(props,text) {
358358- | Ok(t) => {
359359- setTree(_ => t)
360360- Underlying.onChange(Underlying.setState(props,t))
361361- setText(_=> Underlying.serialise(props,t))
362362- setEditing(_=>"off")
363363- }
364364- | Error(e) => {
365365- setEditing(_=>e)
366366- }
367367- }
368368- }
369369- let onChange= (ev: JsxEvent.Form.t) => {
370370- let target = JsxEvent.Form.target(ev)
371371- let value: string = target["value"]
372372- setText(_ => value)
373373- }
374374- if editing == "on" {
375375- <div><input value={text} onChange={onChange}/><div onClick={done}>{React.string("DONE")}</div></div>
376376- } else if editing == "off" {
377377- <div onClick={_ => setEditing(_ => "on")}>
378378- {Underlying.make(Underlying.setState(props,tree))}
379379- </div>
380380- } else {
381381- <div onClick={_ => setEditing(_ => "on")}>{React.string(editing)}</div>
382382- }
383383- }
384384-}
385385-386386-module EditableTextArea = (Underlying : TEXTUAL_COMPONENT) => {
387387- type props = Underlying.props
388388- @react.componentWithProps
389389- let make = props => {
390390- Console.log(("MK",props))
391391- let (editing,setEditing) = React.useState (_ => "off")
392392- let (tree,setTree) = React.useState (_=>Underlying.getState(props))
393393- let (text,setText) =
394394- React.useState (_=> Underlying.serialise(props,tree))
395395- let done = _ => {
396396- switch Underlying.deserialise(props,text) {
397397- | Ok(t) => {
398398- setTree(_ => t)
399399- Underlying.onChange(Underlying.setState(props,t))
400400- setText(_=> Underlying.serialise(props,t))
401401- setEditing(_=>"off")
402402- }
403403- | Error(e) => {
404404- setEditing(_ => e)
405405- }
406406- }
407407- }
408408- let onChange= (ev: JsxEvent.Form.t) => {
409409- let target = JsxEvent.Form.target(ev)
410410- let value: string = target["value"]
411411- setText(_ => value)
412412- }
413413- if editing == "on" {
414414- <div>
415415- <textarea className="editor-textArea" onChange={onChange}>
416416- {React.string(text)}
417417- </textarea>
418418- <div className="editor-controls">
419419- <span className="editor-button button-icon button-icon-blue typcn typcn-tick" onClick={done}>
420420- </span>
421421- <span className="editor-button button-icon button-icon-grey typcn typcn-times" onClick={_ => {
422422- setEditing(_ => "off")
423423- setText(_=> Underlying.serialise(props,tree))
424424- }}>
425425- </span>
426426- </div>
427427- </div>
428428- } else if editing == "off" {
429429- <div>{Underlying.make(Underlying.setState(props,tree))}
430430- <div className="editor-controls">
431431- <span className="editor-button button-icon button-icon-blue typcn typcn-edit" onClick={_ => setEditing(_ => "on")}>
432432- </span>
433433- </div>
434434- </div>
435435- } else {
436436- <div className="editor-error">{React.string(editing)}
437437- <div className="editor-controls">
438438- <span className="editor-button button-icon button-icon-blue typcn typcn-edit"
439439- onClick={_ => setEditing(_ => "on")}>
440440- </span>
441441- <span className="editor-button button-icon button-icon-grey typcn typcn-times" onClick={_ => {
442442- setText(_=> Underlying.serialise(props,tree))
443443- setEditing(_ => "off")
444444- }}>
445445- </span>
446446- </div>
447447- </div>
448448-449449- }
450450- }
451451-}
452452-453453-module PE2 = ProofEngine(SExpBase)
454454-module RuleSExpTE = RuleSetTextualComponent(SExpBase,SExpView,SExpJView)
455455-module RuleSExpView = EditableTextArea(RuleSExpTE)
456456-include PE2.Rule
457457-include RuleSExpView//(RuleSExpView.Hypothetical(RuleSExpView.Inline))
458458-//include SExpBaseView
459459-
···11import * as ComponentGraph2 from './componentgraph'
22-import { make as SExpBaseView, RuleSExpTE } from './Test.mjs'
33-import { parseTopLevel, prettyPrintTopLevel } from './Test.mjs'
22+import { make as SExpBaseView, RuleSExpTE } from './Scratch.mjs'
43import ReactDOM from 'react-dom/client';
54import React from 'react';
65