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.

initial associative-commutative atom definition #1

open opened by joshcbrown.tngl.sh targeting main from joshcbrown.tngl.sh/holbert-ng: assoc-comm-atom

this is a) incomplete, and b) imprecise, given that there's no notion of a carrier set, we just assume it to be int for now, and i haven't settled on the right language to describe the existence of a partial inverse.

anyway, it works for the definition of Nat which i think is a good enough starting point

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:2b3sedxepcp6u7s2najxr2zm/sh.tangled.repo.pull/3mke3eh2ixg22
+624 -69
Diff #0
+57 -39
index.html
··· 47 47 --bound-col: #9343a7; 48 48 --placeholder-fg-col: #aaa; 49 49 --symbol-col: #990000; 50 + --symbol-nat-col: #ffaa00; 50 51 --item-inserting-col: #aa00cc; 51 52 --sidebar-header-col: #c9c9d8; 52 53 --error-bg-col: #ffaaaa; ··· 244 245 .symbol { 245 246 color: var(--symbol-col); 246 247 } 248 + .symbol-nat { 249 + color: var(--symbol-nat-col) 250 + } 247 251 .symbol-turnstile { 248 252 padding-left: 6px; 249 253 padding-right: 6px; ··· 597 601 s. 0 1 |- elim (M-Surround 1 "$s") {}} 598 602 </hol-string-proof> 599 603 <hol-string id="index.html/string-arithmetic" deps="index.html/myconfig"> 600 - -------- nat-z 601 - (Nat Z) 602 - 603 - n. (Nat n) 604 - -------- nat-s 605 - (Nat (S n)) 606 - 607 - n1. 608 - -------- nat-plus-z 609 - (Eval (Plus n1 Z) n1) 610 - 611 - n1. n2. res. 612 - (Eval (Plus n1 n2) res) 613 - --------- nat-plus-s 614 - (Eval (Plus n1 (S n2)) (S res)) 615 - 616 - ------- string-zero 617 - (Parse "0" Z) 618 - 619 - ------- string-one 620 - (Parse "1" (S Z)) 621 - 622 - -------- string-two 623 - (Parse "2" (S (S Z))) 624 - 625 - -------- string-three 626 - (Parse "3" (S (S (S Z)))) 627 - 628 - s1. s2. n2. 629 - (Parse s2 n2) 604 + s. n. 630 605 --------- string-prod 631 - (Parse "$s1 * $s2" (Prod s1 n2)) 606 + (Parse "$s * $n" (Prod s nat(n))) 632 607 633 608 s1. 634 609 ---------- eval-prod-Z 635 - (Eval (Prod s1 Z) "") 610 + (Eval (Prod s1 nat(0)) "") 636 611 637 612 s1. s1n. n. 638 - (Eval (Prod s1 n) s1n) 613 + (Eval (Prod s1 nat(n)) s1n) 639 614 ----------- eval-prod-S 640 - (Eval (Prod s1 (S n)) "$s1$s1n") 615 + (Eval (Prod s1 nat(n + 1)) "$s1$s1n") 641 616 642 617 s. ast. res. 643 618 (Parse s ast) ··· 646 621 (PEval s res) 647 622 648 623 </hol-string> 649 - <hol-string-proof id="index.html/sexp-nat-toy" deps="index.html/myconfig index.html/string-arithmetic"> 650 - ----------- mexp-lexp 651 - (Eval (Plus (S Z) (S (S Z))) (S (S (S Z)))) 652 - |- ? 653 - </hol-string-proof> 654 624 <hol-string-proof id="index.html/string-prod-toy" deps="index.html/myconfig index.html/string-arithmetic"> 655 625 ------------- foo-3 656 626 (PEval "foo * 3" "foo foo foo") 657 627 |- ? 658 628 659 629 </hol-string-proof> 630 + <hol-string id="index.html/types" deps="index.html/myconfig"> 631 + n. (Type n Nat) 632 + ------------ Var 633 + (Var n) 634 + 635 + ------------ Sig-Empty 636 + (Sig Empty) 637 + 638 + f. n. sig. (Sig sig) 639 + ------------ Sig-Cons 640 + (Sig (Cons (f nat(n)) sig)) 641 + 642 + f. n. sig. 643 + (Sig sig) 644 + ------------ InSig-Head 645 + (InSig (f nat(n)) (Cons (f nat(n)) sig)) 646 + 647 + f. n. g. sig. 648 + (Sig sig) 649 + (InSig (f nat(n)) sig) 650 + ------------ InSig-Next 651 + (InSig (f nat(n)) (Cons g (f nat(n)))) 652 + 653 + v. sig. (Sig sig) (Var v) 654 + ------------ Term-Var 655 + (Term sig (Var v)) 656 + 657 + a. sig. (Sig sig) (InSig (a nat(0)) sig) 658 + ------------ Term-Const 659 + (Term sig (Const a)) 660 + 661 + sig. 662 + ------------ Term-List-0 663 + (TermList sig nat(0) Empty) 664 + 665 + f. n. t. ts. sig. (Term sig t) (TermList nat(n) ts) 666 + ------------ Term-List-n 667 + (TermList sig nat(n + 1) (Cons t ts)) 668 + 669 + f. n. ts. sig. (Sig sig) (InSig (f nat(n)) sig) (TermList sig nat(n) ts) 670 + ------------ Term-Func 671 + (Term sig (Func f ts)) 672 + </hol-string> 673 + <hol-string-proof id="index.html/play-sig" deps="index.html/myconfig index.html/types"> 674 + ------------- penips 675 + (Term (Cons (f nat(2)) (Cons (a nat(0)) Empty)) (Func f (Cons (Const a) (Cons (Var nat(1)) Empty)))) 676 + |- ? 677 + </hol-string-proof> 660 678 <hol-string id="index.html/string-silliness" deps="index.html/myconfig"> 661 679 a. 662 680 -------------- eq-refl
+387
src/AssocComm.res
··· 1 + module type AC_ATOM = { 2 + module ACBase: AssocCommBase.AC_BASE_ATOM 3 + include AtomDef.ATOM with module Base = ACBase 4 + let identity: t 5 + let add: (t, t) => t 6 + } 7 + 8 + module Make = ( 9 + BaseAtom: AssocCommBase.AC_BASE_ATOM, 10 + C: { 11 + let coerce: AtomBase.anyValue => option<BaseAtom.t> 12 + }, 13 + ): (AC_ATOM with module ACBase = BaseAtom) => { 14 + module ACBase = BaseAtom 15 + module Base = BaseAtom 16 + module Const = BaseAtom.Const 17 + module IntMap = Belt.Map.Int 18 + type t = Base.t 19 + type subst = Map.t<int, t> 20 + type gen = ref<int> 21 + let identity = Base.identity 22 + let add = (a1: t, a2: t) => { 23 + Base.schemas: IntMap.merge(a1.schemas, a2.schemas, (_, o1, o2) => 24 + switch (o1, o2) { 25 + | (Some({allowed, count}), None) | (None, Some({allowed, count})) => 26 + Some({Base.allowed, count}) 27 + | (Some({allowed, count: count1}), Some({count: count2})) => 28 + Some({allowed, count: count1 + count2}) 29 + | (None, None) => throw(Util.Unreachable("belt map error")) 30 + } 31 + ), 32 + vars: IntMap.merge(a1.vars, a2.vars, (_, o1, o2) => 33 + switch (o1, o2) { 34 + | (Some(count), None) | (None, Some(count)) => Some(count) 35 + | (Some(count1), Some(count2)) => Some(count1 + count2) 36 + | (None, None) => throw(Util.Unreachable("belt map error")) 37 + } 38 + ), 39 + const: a1.const + a2.const, 40 + } 41 + let multiply = (a: t, n: int) => { 42 + Base.schemas: a.schemas->IntMap.map(({allowed, count}) => {Base.allowed, count: count * n}), 43 + vars: a.vars->IntMap.map(count => count * n), 44 + const: a.const * n, 45 + } 46 + let singletonSubst: (int, t) => subst = (schematic, term) => { 47 + let subst = Map.make() 48 + subst->Map.set(schematic, term) 49 + subst 50 + } 51 + let prettyPrint = (t: t, ~scope: array<string>) => { 52 + open Const 53 + let opString = ` ${opString} ` 54 + let schemas = switch t.schemas->IntMap.size { 55 + | 0 => None 56 + | _ => 57 + t.schemas 58 + ->IntMap.toArray 59 + ->Array.map(((schematic, {allowed, count})) => 60 + Util.prettyPrintSchematic(schematic, allowed, scope)->formatMultiple(count) 61 + ) 62 + ->Array.join(opString) 63 + ->Some 64 + } 65 + 66 + let vars = switch t.vars->IntMap.size { 67 + | 0 => None 68 + | _ => 69 + t.vars 70 + ->IntMap.toArray 71 + ->Array.map(((idx, count)) => Util.prettyPrintVar(idx, scope)->formatMultiple(count)) 72 + ->Array.join(opString) 73 + ->Some 74 + } 75 + let const = switch t.const { 76 + | 0 => None 77 + | _ => t.const->formatMultipleConst->Some 78 + } 79 + let inner = [schemas, vars, const]->Array.keepSome->Array.join(opString) 80 + `${openTerm} ${inner} ${closeTerm}` 81 + } 82 + // {0, 1, ..., n - 1}^m 83 + let latticePoints = (n: int, m: int): Seq.t<array<int>> => 84 + Seq.init(n ** m, i => { 85 + let vec = [] 86 + let x = ref(i) 87 + for _ in 0 to m - 1 { 88 + vec->Array.push(x.contents % n) 89 + x := x.contents / n 90 + } 91 + vec 92 + }) 93 + let compWiseGt = (v1: array<int>, v2: array<int>): bool => 94 + Belt.Array.zip(v1, v2)->Array.every(((x, y)) => x > y) 95 + let dot = (v1: array<int>, v2: array<int>): int => 96 + Belt.Array.zipBy(v1, v2, (a, b) => a * b)->Array.reduce(0, (a, b) => a + b) 97 + let unify = (a: t, b: t, ~gen as _=?) => { 98 + let solveNaive = (a: t, b: t) => { 99 + let schema = a.schemas->IntMap.minKey->Option.getExn 100 + Base.Const.opInv(b.const, a.const) 101 + ->Option.map(n => Seq.once(singletonSubst(schema, Base.const(n)))) 102 + ->Option.getOr(Seq.empty) 103 + } 104 + let isNaiveCase = (a: t, b: t): bool => { 105 + a.schemas->IntMap.size == 1 && 106 + b.schemas->IntMap.size == 0 && 107 + a.vars->IntMap.size == 0 && 108 + b.vars->IntMap.size == 0 && 109 + (a.schemas->IntMap.get(a.schemas->IntMap.minKey->Option.getExn)->Option.getExn).count == 1 110 + } 111 + 112 + // steps in elementary algorithm 113 + // 1. gen schema vector `schemas` by lhs - rhs. this has length n 114 + // 2. test all vectors in {0, 1, ..., max(schemas) + 1}^n for eq to 0 115 + // vectors that match should be filtered as we go for being (component-wise) minimal 116 + // 3. reconstruct sub: for each v_i from previous step, 117 + // schema_i |-> sum(v_j[i] * schema_j for j in 0..n) 118 + // 119 + // we /should/ be able to extend this to elemtary w/ constants 120 + // bc that just requires an inhomogeneous eqn solver, but i'm nto so 121 + // sure about the vectors above giving a minimal compelte set of sols 122 + let homogeneousSolve = () => { 123 + let eqnLhs = a.schemas->IntMap.merge(b.schemas, (_, o1, o2) => { 124 + switch (o1, o2) { 125 + | (Some({allowed, count}), None) => Some({Base.allowed, count}) 126 + | (None, Some({allowed, count})) => Some({Base.allowed, count: -count}) 127 + | (Some({allowed, count: count1}), Some({count: count2})) => 128 + Some({allowed, count: count1 - count2}) 129 + | (None, None) => throw(Util.Unreachable("belt map error")) 130 + } 131 + }) 132 + let eqnEntries = eqnLhs->IntMap.toArray 133 + let counts = eqnEntries->Array.map(((_, entry)) => entry.count) 134 + let maxCount = counts->Array.reduce(0, (curMax, next) => max(curMax, abs(next))) 135 + let minimalSols = [] 136 + let addIfMinimal = (sols: array<array<int>>, candidate) => 137 + if sols->Array.every(sol => !(candidate->compWiseGt(sol))) { 138 + sols->Array.push(candidate) 139 + } 140 + latticePoints(maxCount + 1, eqnEntries->Array.length) 141 + // skip [0, ..., 0] (trivial solution to homogeneous eqn) 142 + ->Seq.tail 143 + ->Seq.forEach(vec => { 144 + if dot(vec, counts) == 0 { 145 + minimalSols->addIfMinimal(vec) 146 + } 147 + }) 148 + let solutionToSubst = (sol: array<int>, target: t): Map.t<int, t> => { 149 + eqnEntries 150 + ->Belt.Array.zipBy(sol, ((schematic, _), count) => 151 + if count > 0 { 152 + Some((schematic, multiply(target, count))) 153 + } else { 154 + None 155 + } 156 + ) 157 + ->Array.keepSome 158 + ->Map.fromArray 159 + } 160 + let res = 161 + minimalSols 162 + ->Belt.Array.mapWithIndex((i, sol) => { 163 + let target = switch Belt.Array.get(eqnEntries, i) { 164 + | Some((schematic, {allowed})) => Base.schematic(schematic, allowed) 165 + | None => identity 166 + } 167 + solutionToSubst(sol, target) 168 + }) 169 + ->Array.reduce(Map.make(), (sig1, sig2) => Util.mapUnionWith(sig1, sig2, add)) 170 + res->Seq.once 171 + } 172 + if a.schemas->IntMap.size == 0 && b.schemas->IntMap.size == 0 { 173 + Seq.once(Map.make()) 174 + } else if isNaiveCase(a, b) { 175 + solveNaive(a, b) 176 + } else if isNaiveCase(b, a) { 177 + solveNaive(b, a) 178 + } else if a.const - b.const == 0 { 179 + homogeneousSolve() 180 + } else { 181 + Seq.empty 182 + } 183 + } 184 + 185 + let parse = (str, ~scope: array<string>, ~gen: option<gen>=?) => { 186 + open Parser 187 + let var = 188 + Const.parseVar(~scope) 189 + ->map(Base.var) 190 + ->label("var") 191 + let schemaLit = 192 + Const.parseSchema(~gen?, ~scope) 193 + ->map(((schematic, allowed)) => Base.schematic(schematic, allowed)) 194 + ->label("schematic") 195 + let const = 196 + Const.parseConst 197 + ->map(Base.const) 198 + ->label("const") 199 + let factor = choice([var, schemaLit, const])->lexeme 200 + let term = 201 + factor->bind(f => 202 + many(token(Const.opString)->then(factor))->map(fs => fs->Array.reduce(f, add)) 203 + ) 204 + let full = term->between(token(Const.openTerm), token(Const.closeTerm)) 205 + Parser.runParser(full, str) 206 + } 207 + 208 + let substitute = (atom: t, subst: subst): t => { 209 + let substituted = 210 + atom.schemas 211 + ->IntMap.toArray 212 + ->Array.map(((key, {allowed, count})) => 213 + switch Map.get(subst, key) { 214 + | None => multiply(Base.schematic(key, allowed), count) 215 + | Some(sub) => multiply(sub, count) 216 + } 217 + ) 218 + ->Array.reduce(identity, add) 219 + substituted->add({...identity, vars: atom.vars, const: atom.const}) 220 + } 221 + let substDeBruijn = (atom: t, substs: array<option<t>>, ~from: int=0): t => { 222 + let substituted = 223 + atom.vars 224 + ->IntMap.toArray 225 + ->Array.map(((idx, count)) => 226 + if idx < from { 227 + multiply(Base.var(idx), count) 228 + } else if idx - from < Array.length(substs) { 229 + switch substs[idx - from]->Option.getExn { 230 + | Some(a) => multiply(a, count) 231 + | None => 232 + throw(SExp.SubstNotCompatible(`index ${Int.toString(idx - from)} not of sort blah`)) 233 + } 234 + } else { 235 + multiply(Base.var(idx - Array.length(substs)), count) 236 + } 237 + ) 238 + ->Array.reduce(identity, add) 239 + let schemas = atom.schemas->IntMap.map(({count, allowed}) => { 240 + Base.count, 241 + allowed: allowed->Array.filterMap(i => 242 + if i < from + Array.length(substs) { 243 + None 244 + } else { 245 + Some(i - (from + Array.length(substs))) 246 + } 247 + ), 248 + }) 249 + substituted->add({...identity, schemas, const: atom.const}) 250 + } 251 + let concrete = (atom: t) => atom.vars->IntMap.size > 0 || atom.const > 0 252 + 253 + let upshift = (atom: t, amount: int, ~from: int=0) => { 254 + let vars = 255 + atom.vars 256 + ->IntMap.toArray 257 + ->Array.map(((idx, count)) => 258 + if idx >= from { 259 + (idx + amount, count) 260 + } else { 261 + (idx, count) 262 + } 263 + ) 264 + ->IntMap.fromArray 265 + let schemas = atom.schemas->IntMap.map(({allowed, count}) => { 266 + Base.count, 267 + allowed: allowed->Array.map(i => 268 + if i >= from { 269 + i + amount 270 + } else { 271 + i 272 + } 273 + ), 274 + }) 275 + {Base.vars, schemas, const: atom.const} 276 + } 277 + let coerce = C.coerce 278 + } 279 + 280 + module MakeView = ( 281 + Base: AssocCommBase.AC_BASE_ATOM, 282 + Atom: AtomDef.ATOM with module Base = Base, 283 + ) => { 284 + module Const = Base.Const 285 + type props = {atom: Atom.t, scope: array<string>} 286 + let renderIf = (component, cond) => 287 + if cond { 288 + component 289 + } else { 290 + React.null 291 + } 292 + module Var = { 293 + type props = {idx: int, count?: int, scope: array<string>} 294 + let make = (props: props) => { 295 + let count = 296 + props.count 297 + ->Option.map(n => React.int(n)->renderIf(n > 1)) 298 + ->Option.getOr(React.null) 299 + switch props.scope[props.idx] { 300 + | Some(n) if Array.indexOf(props.scope, n) == props.idx => 301 + <span className="term-metavar"> 302 + {count} 303 + {React.string(n)} 304 + </span> 305 + | _ => 306 + <span className="term-metavar-unnamed"> 307 + {count} 308 + {React.string("\\")} 309 + {React.int(props.idx)} 310 + </span> 311 + } 312 + } 313 + } 314 + 315 + let parenthesise = f => 316 + React.array([ 317 + <span className="symbol-nat" key={"-1"}> {React.string("(")} </span>, 318 + f, 319 + <span className="symbol-nat" key={"-2"}> {React.string(")")} </span>, 320 + ]) 321 + 322 + let intersperseOp = (a: array<React.element>) => 323 + Util.intersperse(a, ~with=React.string(Const.opString))->React.array 324 + let make = ({atom, scope}: props) => { 325 + let schemas = switch atom.schemas->Belt.Map.Int.size { 326 + | 0 => None 327 + | _ => 328 + atom.schemas 329 + ->Belt.Map.Int.toArray 330 + ->Array.map(((schematic, {allowed, count})) => 331 + <span className="term-schematic"> 332 + {React.string(`${count->Int.toString}?`)} 333 + {React.int(schematic)} 334 + <span className="term-schematic-telescope"> 335 + {allowed 336 + ->Array.mapWithIndex((idx, _) => <Var idx scope />) 337 + ->intersperseOp} 338 + </span> 339 + </span> 340 + ) 341 + ->React.array 342 + ->Some 343 + } 344 + let vars = switch atom.vars->Belt.Map.Int.size { 345 + | 0 => None 346 + | _ => 347 + atom.vars 348 + ->Belt.Map.Int.toArray 349 + ->Array.map(((idx, count)) => <Var idx scope count key={idx->Int.toString} />) 350 + ->intersperseOp 351 + ->Some 352 + } 353 + let const = switch (schemas, vars) { 354 + | (_, Some(_)) | (Some(_), _) if atom.const > 0 => Some(React.int(atom.const)) 355 + | (None, None) => Some(React.int(atom.const)) 356 + | _ => None 357 + } 358 + <span className="term-compound"> 359 + {[schemas, vars, const]->Array.keepSome->intersperseOp->parenthesise} 360 + </span> 361 + } 362 + } 363 + 364 + module Nat = { 365 + module Base = AssocCommBase.Nat 366 + module Atom = Make( 367 + AssocCommBase.Nat, 368 + { 369 + let coerce = (AtomBase.AnyValue(tag, a)) => 370 + switch tag { 371 + | Symbolic.Base.Tag => Int.fromString(a)->Option.map(Base.const) 372 + | AtomBase.VarBase.Tag => 373 + switch a { 374 + | Var({idx}) => Some(Base.var(idx)) 375 + | Schematic({schematic, allowed}) => Some(Base.schematic(schematic, allowed)) 376 + } 377 + | AtomBase.String.Tag => 378 + switch a { 379 + | [String(s)] => Int.fromString(s)->Option.map(Base.const) 380 + | _ => None 381 + } 382 + | _ => None 383 + } 384 + }, 385 + ) 386 + module AtomView = MakeView(AssocCommBase.Nat, Atom) 387 + }
+74
src/AssocCommBase.res
··· 1 + module type AC_CONST = { 2 + let op: (int, int) => int 3 + let opInv: (int, int) => option<int> 4 + let opString: string 5 + let openTerm: string 6 + let closeTerm: string 7 + let formatMultiple: (string, int) => string 8 + let formatMultipleConst: int => string 9 + let parseConst: Parser.t<int> 10 + let parseVar: (~scope: array<string>) => Parser.t<int> 11 + let parseSchema: (~gen: ref<int>=?, ~scope: array<string>) => Parser.t<(int, array<int>)> 12 + } 13 + 14 + module type AC_BASE_ATOM = { 15 + module Const: AC_CONST 16 + type base = { 17 + schemas: Belt.Map.Int.t<{allowed: array<int>, count: int}>, 18 + vars: Belt.Map.Int.t<int>, 19 + const: int, 20 + } 21 + let identity: base 22 + let const: int => base 23 + let var: int => base 24 + let schematic: (int, array<int>) => base 25 + include module type of AtomBase.Make({ 26 + type t = base 27 + }) 28 + } 29 + 30 + module Make = (Const: AC_CONST): AC_BASE_ATOM => { 31 + module Const = Const 32 + type base = { 33 + schemas: Belt.Map.Int.t<{allowed: array<int>, count: int}>, 34 + vars: Belt.Map.Int.t<int>, 35 + const: int, 36 + } 37 + module IntMap = Belt.Map.Int 38 + let identity = {schemas: IntMap.empty, vars: IntMap.empty, const: 0} 39 + let const = n => {...identity, const: n} 40 + let var = idx => {...identity, vars: IntMap.empty->IntMap.set(idx, 1)} 41 + let schematic = (schematic, allowed) => { 42 + ...identity, 43 + schemas: IntMap.empty->IntMap.set(schematic, {allowed, count: 1}), 44 + } 45 + include AtomBase.Make({type t = base}) 46 + } 47 + 48 + module Nat = Make({ 49 + let op = (a, b) => a + b 50 + let opInv = (a, b) => 51 + if a - b < 0 { 52 + None 53 + } else { 54 + Some(a - b) 55 + } 56 + let opString: string = "+" 57 + let openTerm: string = "nat(" 58 + let closeTerm: string = ")" 59 + let formatMultiple: (string, int) => string = (s, n) => `${n->Int.toString}${s}` 60 + let formatMultipleConst: int => string = n => Int.toString(n) 61 + let parseConst: Parser.t<int> = Parser.decimal 62 + let parseVar = (~scope) => Parser.Util.varIdx(~scope) 63 + let seen = (g: ref<int>, s: int) => { 64 + if s >= g.contents { 65 + g := s + 1 66 + } 67 + } 68 + let parseSchema = (~gen=?, ~scope: array<string>): Parser.t<(int, array<int>)> => { 69 + Parser.Util.schemaLit(~scope)->Parser.map(((schematic, allowed)) => { 70 + gen->Option.map(g => allowed->Array.forEach(n => seen(g, n)))->ignore 71 + (schematic, allowed) 72 + }) 73 + } 74 + })
-1
src/Method.res
··· 335 335 if cur.contents->String.get(0) == Some("}") { 336 336 cur := String.trim(cur.contents->String.sliceToEnd(~start=1)) 337 337 let res = {ruleName, elimName, instantiation, subgoals} 338 - Console.log(("parsed elim", res)) 339 338 Ok((res, cur.contents)) 340 339 } else { 341 340 Error("} or subgoal proof expected")
+30 -1
src/Parser.res
··· 106 106 } 107 107 ) 108 108 )->map(List.toArray) 109 + let sepBy = (t, delim) => t->bind(p => many(delim->then(t))->map(ps => Array.concat([p], ps))) 109 110 let between = (inner: t<'a>, o1: t<string>, o2: t<string>): t<'a> => o1->then(inner)->thenIgnore(o2) 110 111 let consume = (l: int): t<string> => 111 112 getCurrentStr->bind(str => { ··· 142 143 if str->String.startsWith(s) { 143 144 consume(String.length(s))->then(pure(s)) 144 145 } else { 145 - expected([`literal ${s}`]) 146 + expected([`"${s}"`]) 146 147 } 147 148 ) 148 149 ··· 212 213 Console.log(("successfully parsed", res)) 213 214 dbgInfo("exit")->map(_ => res) 214 215 }) 216 + ->mapError(err => { 217 + Console.log(("error", infoPretty(err.info))) 218 + err 219 + }) 215 220 } 216 221 217 222 let lexeme = p => p->thenIgnore(regex(/^\s*/)->void) ··· 235 240 ~scope: array<'m>, 236 241 ~gen: option<'g>=?, 237 242 ): t<'a> => lift(s => f(s, ~scope, ~gen?)) 243 + 244 + module Util = { 245 + let varLit = string("\\")->then(decimal)->lexeme 246 + let ident = regex1(/([^\s\(\)]+)/)->lexeme 247 + let varIdx = (~scope: array<string>) => 248 + varLit->or( 249 + ident->bind(id => 250 + switch scope->Array.indexOfOpt(id) { 251 + | Some(idx) => pure(idx) 252 + | None => expected(["in-scope variable"]) 253 + } 254 + ), 255 + ) 256 + let schemaLit = (~scope: array<string>) => 257 + string("?") 258 + ->then(decimal) 259 + ->bind(schematic => 260 + many(varIdx(~scope)) 261 + ->between(token("("), token(")")) 262 + ->map(allowed => { 263 + (schematic, allowed) 264 + }) 265 + ) 266 + }
+5 -24
src/SExp.res
··· 267 267 } 268 268 let mkParser = (~scope: array<string>, ~gen=?): Parser.t<t> => { 269 269 open Parser 270 - let varLit = string("\\")->then(decimal) 271 - let ident = regex1(/([^\s\(\)]+)/) 272 - let varIdx = 273 - varLit 274 - ->or( 275 - ident->bind(id => 276 - switch scope->Array.indexOfOpt(id) { 277 - | Some(idx) => pure(idx) 278 - | None => fail("expected variable") 279 - } 280 - ), 281 - ) 282 - ->lexeme 270 + let varIdx = Parser.Util.varIdx(~scope) 283 271 let var = varIdx->map(idx => Var({idx: idx})) 272 + let schemaLit = Parser.Util.schemaLit(~scope)->Parser.map(((schematic, allowed)) => { 273 + gen->Option.map(g => allowed->Array.forEach(n => seen(g, n)))->ignore 274 + Schematic({schematic, allowed}) 275 + }) 284 276 285 - let schemaLit = 286 - string("?") 287 - ->then(decimal) 288 - ->bind(schematic => 289 - many(varIdx) 290 - ->between(token("("), token(")")) 291 - ->map(allowed => { 292 - gen->Option.map(g => allowed->Array.forEach(n => seen(g, n)))->ignore 293 - Schematic({schematic, allowed}) 294 - }) 295 - ) 296 277 let inner = fix(f => 297 278 choice([ 298 279 schemaLit->label("schematic"),
+9 -3
src/Scratch.res
··· 54 54 Symbol.Atom, 55 55 Symbol.AtomView, 56 56 ) 57 - module StringSExp = SExp.Make(StringSymbol.Atom) 58 - module TermView = SExpView.Make(StringSymbol.Atom, StringSymbol.AtomView, StringSExp) 57 + module StringNatSymbol = AtomDef.MakeAtomChoiceAndView( 58 + AssocComm.Nat.Atom, 59 + AssocComm.Nat.AtomView, 60 + StringSymbol.Atom, 61 + StringSymbol.AtomView, 62 + ) 63 + module StringSExp = SExp.Make(StringNatSymbol.Atom) 64 + module TermView = SExpView.Make(StringNatSymbol.Atom, StringNatSymbol.AtomView, StringSExp) 59 65 module StringSExpJView = TermViewAsJudgmentView.Make(StringSExp, StringSExp, TermView) 60 66 module AxiomStr = Editable.TextArea( 61 - StringAxiomSet.Make(StringSymbol.Atom, StringSExp, StringSExpJView), 67 + StringAxiomSet.Make(StringNatSymbol.Atom, StringSExp, StringSExpJView), 62 68 ) 63 69 64 70 module DerivationsOrLemmasStrView = MethodView.CombineMethodView(
+8
src/StringA.res
··· 471 471 | Schematic({schematic, allowed}) => Schematic({schematic, allowed}) 472 472 }, 473 473 ]) 474 + | AssocCommBase.Nat.Tag => { 475 + module IntMap = Belt.Map.Int 476 + if a.schemas->IntMap.size == 0 && a.vars->IntMap.size == 0 { 477 + Some([String(Int.toString(a.const))]) 478 + } else { 479 + None 480 + } 481 + } 474 482 | _ => None 475 483 } 476 484 }
+4
src/Util.res
··· 108 108 ->Iterator.toArrayWithMapper(((i, v)) => f(i, v)->Option.map(v => (i, v))) 109 109 ->Array.keepSome 110 110 ->Map.fromArray 111 + let update = (m: t<'k, 'v>, k: 'k, f: 'v => 'v, ~default: 'v) => { 112 + m->Map.set(k, Map.get(m, k)->Option.map(f)->Option.getOr(default)) 113 + } 114 + let clone = (m: t<'k, 'v>) => m->Map.entries->Map.fromIterator 111 115 } 112 116 113 117 let arrayWithIndex = (arr: array<React.element>) => {
+49
tests/NatTest.res
··· 1 + open Zora 2 + 3 + module NatBase = AssocCommBase.Nat 4 + module Nat = AssocComm.Nat.Atom 5 + module Util = TestUtil.MakeAtomTester(Nat) 6 + module ParseUtil = Util.ParseTester 7 + module UnifyUtil = Util.UnifyTester 8 + 9 + module IntMap = Belt.Map.Int 10 + let const = NatBase.const 11 + let var = NatBase.var 12 + let schematic = NatBase.schematic 13 + 14 + zoraBlock("parse nat", t => { 15 + let testParse = (t, str, expected, ~scope=?) => 16 + t->ParseUtil.testParse(`nat(${str})`, expected, ~scope?) 17 + let testParseFail = (t, str, ~scope=?) => t->ParseUtil.testParseFail(`nat(${str})`, ~scope?) 18 + t->block("const", t => { 19 + t->block("single digit", t => t->testParse("1", const(1))) 20 + t->block("multi digit", t => t->testParse("123", const(123))) 21 + t->block("negative", t => t->testParseFail("-1")) 22 + }) 23 + t->block("var", t => { 24 + t->block("in scope", t => t->testParse("x", var(0), ~scope=["x"])) 25 + t->block("not in scope", t => t->testParseFail("x")) 26 + t->block("const", t => t->testParse(`\\1`, var(1))) 27 + t->block("const multi digit", t => t->testParse(`\\10`, var(10))) 28 + }) 29 + t->block("schematic", t => { 30 + t->block("valid empty allowed", t => t->testParse(`?1()`, schematic(1, []))) 31 + t->block("invalid no allowed", t => t->testParseFail(`?1`)) 32 + t->block("valid empty allowed multi digit", t => t->testParse(`?10()`, schematic(10, []))) 33 + t->block( 34 + "valid with non-empty allowed", 35 + t => t->testParse(`?10(\\1 \\23 \\4)`, schematic(10, [1, 23, 4])), 36 + ) 37 + }) 38 + t->block("single add", t => t->testParse("1 + 2", Nat.add(const(1), const(2)))) 39 + t->block("multi add", t => { 40 + t->testParse("1 + 2 + x", Nat.add(Nat.add(const(1), const(2)), var(0)), ~scope=["x"]) 41 + t->testParse("?1(x) + ?1()", Nat.add(schematic(1, [0]), schematic(1, [])), ~scope=["x"]) 42 + }) 43 + }) 44 + 45 + zoraBlock("unify nat", t => { 46 + let parse = s => Nat.parse(`nat(${s})`, ~scope=[])->Result.getExn->Pair.first 47 + t->block("test", t => t->UnifyUtil.testUnify(parse("?1()"), parse("?2()"))) 48 + t->block("test", t => t->UnifyUtil.testUnify(parse("?1() + ?2()"), parse("?3() + ?3()"))) 49 + })
+1 -1
tests/ParserTests.res
··· 5 5 switch P.runParser(p, str) { 6 6 | Ok((res, "")) => expect->Option.map(expect => t->equal(res, expect))->ignore 7 7 | Ok((_, rem)) => t->fail(~msg=`failed to consume remaining input: ${rem}`) 8 - | Error(e) => t->fail(~msg=`parse failed`) 8 + | Error(msg) => t->fail(~msg=`parse failed: ${msg}`) 9 9 } 10 10 } 11 11

History

2 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
initial associative-commutative atom definition
no conflicts, ready to merge
expand 0 comments
1 commit
expand
initial associative-commutative atom definition
expand 0 comments