diff --git a/CHANGES.md b/CHANGES.md index b71a20ae617b283b2021c8b7a59e2543de53cd74..3c8fec63a70518c5abad966eb7a2fd8897c5114f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Change List +## 2024-07-22 +Resealed the `Proof` trait following a fix of the relevant compiler bug [scala/scala3#19031](https://github.com/scala/scala3/issues/19031). + +Updated to Scala 3.4.2, and relevant minor syntax changes from `-rewrite -source 3.4-migration`. + ## 2024-04-12 Addition of the Congruence tactic, solving sequents by congruence closure using egraphs. diff --git a/build.sbt b/build.sbt index a38f440c5b80478e6f07c9ea140850aaf88d01b9..faa922e1e7c8215109d83f2c8c64f552f7c769a6 100644 --- a/build.sbt +++ b/build.sbt @@ -15,14 +15,14 @@ ThisBuild / javacOptions ++= Seq("-encoding", "UTF-8") ThisBuild / semanticdbEnabled := true ThisBuild / semanticdbVersion := scalafixSemanticdb.revision +val scala2 = "2.13.8" +val scala3 = "3.4.2" + val commonSettings = Seq( - crossScalaVersions := Seq("3.3.3"), + crossScalaVersions := Seq(scala3, scala2), run / fork := true ) -val scala2 = "2.13.8" -val scala3 = "3.3.3" - val commonSettings2 = commonSettings ++ Seq( scalaVersion := scala2, scalacOptions ++= Seq("-Ypatmat-exhaust-depth", "50") @@ -30,7 +30,7 @@ val commonSettings2 = commonSettings ++ Seq( val commonSettings3 = commonSettings ++ Seq( scalaVersion := scala3, scalacOptions ++= Seq( - "-language:implicitConversions", + "-language:implicitConversions" ), javaOptions += "-Xmx10G", libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.18" % "test", diff --git a/lisa-sets/src/main/scala/lisa/automation/Apply.scala b/lisa-sets/src/main/scala/lisa/automation/Apply.scala index a0c22d7902b93a43077c45b2cb160993e506eb84..eb3896284829c6d5133dc275abea8ff51a0f8fa0 100644 --- a/lisa-sets/src/main/scala/lisa/automation/Apply.scala +++ b/lisa-sets/src/main/scala/lisa/automation/Apply.scala @@ -112,7 +112,7 @@ class Apply(using val lib: Library, val proof: lib.Proof)(thm: proof.Fact) exten * @param tSubst the assignment for term variables */ private def substitute(using _proof: lib.Proof)(fact: _proof.Fact, fSubst: FormulaSubstitution, tSubst: TermSubstitution): _proof.Fact = - fact.of(fSubst.toFSubstPair: _*).of(tSubst.toTSubstPair: _*) + fact.of(fSubst.toFSubstPair*).of(tSubst.toTSubstPair*) /** * Applies on method with a varargs instead of a sequence. diff --git a/lisa-sets/src/main/scala/lisa/automation/CommonTactics.scala b/lisa-sets/src/main/scala/lisa/automation/CommonTactics.scala index c5b8e80a2cd9c2908dddbd36c43a3b6e1699dd5d..a81e8d9ac610529e5518ff8eb7aaf7472104bbf2 100644 --- a/lisa-sets/src/main/scala/lisa/automation/CommonTactics.scala +++ b/lisa-sets/src/main/scala/lisa/automation/CommonTactics.scala @@ -178,14 +178,14 @@ object CommonTactics { // Instantiate terms in the definition val subst = vars.zip(xs).map(tup => tup._1 := tup._2) - val P = definition.f.substitute(subst: _*) + val P = definition.f.substitute(subst*) val expected = P.substitute(y := fxs) if (!F.isSame(expected, bot.right.head)) { return proof.InvalidProofTactic("Right-hand side of bottom sequent should be of the form P(f(xs)).") } TacticSubproof { - lib.have(F.∀(y, (y === fxs) <=> P)) by Tautology.from(uniqueness, definition.of(subst: _*)) + lib.have(F.∀(y, (y === fxs) <=> P)) by Tautology.from(uniqueness, definition.of(subst*)) lib.thenHave((y === fxs) <=> P) by InstantiateForall(y) lib.thenHave((fxs === fxs) <=> P.substitute(y := fxs)) by InstFunSchema(Map(y -> fxs)) lib.thenHave(P.substitute(y := fxs)) by Restate @@ -218,7 +218,7 @@ object CommonTactics { // val instantiations: Seq[(F.SchematicTermLabel, F.LambdaTermTerm)] = vars.zip(xs.map(x => F.LambdaTermTerm(Seq(), x))) val subst = vars.zip(xs).map(tup => tup._1 := tup._2) - val P = definition.f.substitute(subst: _*) + val P = definition.f.substitute(subst*) // Instantiate terms in the definition // val P = F.LambdaTermFormula(Seq(y), expr(xs)) @@ -248,7 +248,7 @@ object CommonTactics { } TacticSubproof { - lib.have(F.∀(y, (y === fxs) <=> P)) by Tautology.from(uniqueness, definition.of(subst: _*)) + lib.have(F.∀(y, (y === fxs) <=> P)) by Tautology.from(uniqueness, definition.of(subst*)) lib.thenHave((y === fxs) <=> P) by InstantiateForall(y) lib.thenHave((fxs === fxs) <=> P.substitute(y := fxs)) by InstFunSchema(Map(y -> fxs)) lib.thenHave(P.substitute(y := fxs)) by Restate diff --git a/lisa-sets/src/main/scala/lisa/automation/Substitution.scala b/lisa-sets/src/main/scala/lisa/automation/Substitution.scala index fcc4564297b0e76faef9198a100a05ec63d463d3..79795e57d8c7cca0827fa68c43420ad17caab6b5 100644 --- a/lisa-sets/src/main/scala/lisa/automation/Substitution.scala +++ b/lisa-sets/src/main/scala/lisa/automation/Substitution.scala @@ -236,7 +236,7 @@ object Substitution { leftContextReduced.termRules.map { case (_, (rule, subst)) => sourceOf.get(rule) match { case Some(f: proof.Fact) => - f.of(subst.toSeq.map((l, r) => (l := r)): _*) + f.of(subst.toSeq.map((l, r) => (l := r))*) // case Some(j: lib.theory.Justification) => // j.of(subst.toSeq.map((l, r) => (l, lambda(Seq(), r))): _*) case _ => @@ -246,7 +246,7 @@ object Substitution { leftContextReduced.formulaRules.map { case (_, (rule, subst)) => sourceOf.get(rule) match { case Some(f: proof.Fact) => - f.of(subst._1.toSeq.map((l, r) => (l := r)) ++ subst._2.toSeq.map((l, r) => (l := r)): _*) + f.of(subst._1.toSeq.map((l, r) => (l := r)) ++ subst._2.toSeq.map((l, r) => (l := r))*) // case Some(j: lib.theory.Justification) => // j.of(subst._1.toSeq.map((l, r) => (l, lambda(Seq[Variable](), r))) ++ subst._2.toSeq.map((l, r) => (l, lambda(Seq[Variable](), r))): _*) case _ => @@ -257,7 +257,7 @@ object Substitution { rightContextReduced.termRules.map { case (_, (rule, subst)) => sourceOf.get(rule) match { case Some(f: proof.Fact) => - f.of(subst.toSeq.map((l, r) => (l := r)): _*) + f.of(subst.toSeq.map((l, r) => (l := r))*) // case Some(j: lib.theory.Justification) => // j.of(subst.toSeq.map((l, r) => (l, lambda(Seq(), r))): _*) case None => @@ -267,7 +267,7 @@ object Substitution { rightContextReduced.formulaRules.map { case (_, (rule, subst)) => sourceOf.get(rule) match { case Some(f: proof.Fact) => - f.of(subst._1.toSeq.map((l, r) => (l := r)) ++ subst._2.toSeq.map((l, r) => (l := r)): _*) + f.of(subst._1.toSeq.map((l, r) => (l := r)) ++ subst._2.toSeq.map((l, r) => (l := r))*) // case Some(j: lib.theory.Justification) => // j.of(subst._1.toSeq.map((l, r) => (l, lambda(Seq[Variable](), r))) ++ subst._2.toSeq.map((l, r) => (l, lambda(Seq[Variable](), r))): _*) case None => diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/types/TypeSystem.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/types/TypeSystem.scala index 850103caa9d80adacaeb8202a08a38684113cb9f..ff939e4dcc5d458d3e59f5817b00f92d5be389a7 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/types/TypeSystem.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/types/TypeSystem.scala @@ -40,7 +40,7 @@ object TypeLib extends lisa.Main { // C |> D is the functional class of functionals from the class C to the class D // F is C |> D desugars into ∀(x, (x is C) => (F(x) is D)) - val testTheorem = Theorem((x is A, f is (A |=> B), F is (A |=> B) |> (A |=> B) ) |- (F(f)*(x) is B)) { + val testTheorem = Theorem((x `is` A, f `is` (A |=> B), F `is` (A |=> B) |> (A |=> B) ) |- (F(f)*(x) `is` B)) { have(thesis) by TypeChecker.prove } @@ -87,7 +87,7 @@ object TypeSystem { case class FunctionalClass(in: Seq[Class], args: Seq[Variable], out: Class, arity: Int) { def formula[N <: Arity](f: (Term**N |-> Term)): Formula = - val inner = (args.zip(in.toSeq).map((term, typ) => (term is typ).asFormula).reduceLeft((a, b) => a /\ b)) ==> (f.applySeq(args) is out) + val inner = (args.zip(in.toSeq).map((term, typ) => (term `is` typ).asFormula).reduceLeft((a, b) => a /\ b)) ==> (f.applySeq(args) `is` out) args.foldRight(inner)((v, form) => forall(v, form)) override def toString(): String = in.map(_.toStringSeparated()).mkString("(", ", ", ")") + " |> " + out.toStringSeparated() @@ -112,8 +112,8 @@ object TypeSystem { } extension [A <: Class](t: Term) { - def is(clas:A): Formula with TypeAssignment[A] = TypeAssignment(t, clas).asInstanceOf[Formula with TypeAssignment[A]] - def ::(clas:A): Formula with TypeAssignment[A] = TypeAssignment(t, clas).asInstanceOf[Formula with TypeAssignment[A]] + def is(clas:A): Formula & TypeAssignment[A] = TypeAssignment(t, clas).asInstanceOf[Formula & TypeAssignment[A]] + def ::(clas:A): Formula & TypeAssignment[A] = TypeAssignment(t, clas).asInstanceOf[Formula & TypeAssignment[A]] def @@(t2: Term): AppliedFunction = AppliedFunction(t, t2) def *(t2: Term): AppliedFunction = AppliedFunction(t, t2) } @@ -318,7 +318,7 @@ object TypeSystem { extension (c: Constant) { def typedWith[A <: Class](typ:A)(justif: JUSTIFICATION) : TypedConstant[A] = - if justif.statement.right.size != 1 || justif.statement.left.size != 0 || !K.isSame((c is typ).asFormula.underlying, justif.statement.right.head.underlying) then + if justif.statement.right.size != 1 || justif.statement.left.size != 0 || !K.isSame((c `is` typ).asFormula.underlying, justif.statement.right.head.underlying) then throw new IllegalArgumentException(s"A proof of typing of $c must be of the form ${c :: typ}, but the given justification shows ${justif.statement}.") else TypedConstant(c.id, typ, justif) } @@ -345,7 +345,7 @@ object TypeSystem { var typingError: proof.ProofTacticJudgement = null bot.right.find(goal => goal match - case (term is typ) => + case (term `is` typ) => val ptj = typecheck(using SetTheoryLibrary)(context.toSeq, term, Some(typ)) if ptj.isValid then success = ptj @@ -378,21 +378,21 @@ object TypeSystem { def innerTypecheck(context2: Map[Term, Seq[Class]], term:Term, typ:Option[Class]): Class= { val possibleTypes = typingAssumptions.getOrElse(term, Nil) if typ == Some(any) then - have(term is any) by Restate.from(TypeLib.any.definition of (x := term)) + have(term `is` any) by Restate.from(TypeLib.any.definition of (x := term)) any else if typ.isEmpty && possibleTypes.size >=1 then - have(term is possibleTypes.head) by Restate + have(term `is` possibleTypes.head) by Restate possibleTypes.head else if (typ.nonEmpty && possibleTypes.contains(typ.get)) then - have(term is typ.get) by Restate + have(term `is` typ.get) by Restate typ.get else term match case tc: TypedConstant[?] => if typ.isEmpty then - have(tc is tc.typ) by Restate.from(tc.justif) + have(tc `is` tc.typ) by Restate.from(tc.justif) tc.typ - else if K.isSame((tc is typ.get).asFormula.underlying, (tc is tc.typ).asFormula.underlying) then - have(tc is typ.get) by Restate.from(tc.justif) + else if K.isSame((tc `is` typ.get).asFormula.underlying, (tc `is` tc.typ).asFormula.underlying) then + have(tc `is` typ.get) by Restate.from(tc.justif) typ.get else throw TypingException("Constant " + tc + " expected to be of type " + typ + " but has type " + tc.typ + ".") @@ -404,8 +404,8 @@ object TypeSystem { funcType match case inType |=> outType => typ match case None => - if K.isSame((arg is inType).asFormula.underlying, (arg is argType).asFormula.underlying) then - have(term is outType) by Tautology.from( + if K.isSame((arg `is` inType).asFormula.underlying, (arg `is` argType).asFormula.underlying) then + have(term `is` outType) by Tautology.from( funcspaceAxiom of (f := func, x := arg, A:= inType, B:= outType), funcProof, argProof @@ -413,9 +413,9 @@ object TypeSystem { outType else throw TypingException("Function " + func + " found to have type " + funcType + ", but argument " + arg + " has type " + argType + " instead of expected " + inType + ".") - case Some(typ) if K.isSame((term is typ).asFormula.underlying, (term is outType).asFormula.underlying) => - if K.isSame((arg is inType).asFormula.underlying, (arg is argType).asFormula.underlying) then - have(term is outType) by Tautology.from( + case Some(typ) if K.isSame((term `is` typ).asFormula.underlying, (term `is` outType).asFormula.underlying) => + if K.isSame((arg `is` inType).asFormula.underlying, (arg `is` argType).asFormula.underlying) then + have(term `is` outType) by Tautology.from( funcspaceAxiom of (f := func, x := arg, A:= inType, B:= outType), funcProof, argProof @@ -450,7 +450,7 @@ object TypeSystem { labelTypes.find((labelType, step) => labelType.arity == args.size && (args zip argTypes).zip(labelType.in.toSeq).forall((argAndTypes, inType) => - K.isSame((argAndTypes._1 is inType).asFormula.underlying, (argAndTypes._1 is argAndTypes._2).asFormula.underlying) // + K.isSame((argAndTypes._1 `is` inType).asFormula.underlying, (argAndTypes._1 `is` argAndTypes._2).asFormula.underlying) // ) ) match case None => @@ -461,28 +461,28 @@ object TypeSystem { val in: Seq[Class] = labelType.in.toSeq //val labelProp = labelType.formula(label.asInstanceOf) val labelPropStatement = step() - val labInst = labelPropStatement.of(args: _*) + val labInst = labelPropStatement.of(args*) val subst = (labelType.args zip args).map((v, a) => (v := a)) val newOut: Class = out match { - case t: Term => t.substitute(subst: _*) - case f: (Term**1 |-> Formula) @unchecked => f.substitute(subst: _*) + case t: Term => t.substitute(subst*) + case f: (Term**1 |-> Formula) @unchecked => f.substitute(subst*) } - have(term is newOut) by Tautology.from( - (argTypesProofs :+ labInst ) : _* + have(term `is` newOut) by Tautology.from( + (argTypesProofs :+ labInst )* ) newOut case Some(typValue) => labelTypes.find((labelType, step) => labelType.arity == args.size && (args zip argTypes).zip(labelType.in.toSeq).forall((argAndTypes, inType) => - K.isSame((argAndTypes._1 is inType).asFormula.underlying, (argAndTypes._1 is argAndTypes._2).asFormula.underlying) + K.isSame((argAndTypes._1 `is` inType).asFormula.underlying, (argAndTypes._1 `is` argAndTypes._2).asFormula.underlying) ) && { val subst = (labelType.args zip args).map((v, a) => (v := a)) val newOut: Class = labelType.out match { - case t: Term => t.substitute(subst: _*) - case f: (Term**1 |-> Formula) @unchecked => f.substitute(subst: _*) + case t: Term => t.substitute(subst*) + case f: (Term**1 |-> Formula) @unchecked => f.substitute(subst*) } - K.isSame((term is newOut).asFormula.underlying, (term is typValue).asFormula.underlying) + K.isSame((term `is` newOut).asFormula.underlying, (term `is` typValue).asFormula.underlying) } ) match @@ -493,8 +493,8 @@ object TypeSystem { val in: Seq[Class] = labelType.in.toSeq //val labelProp = labelType.formula(label.asInstanceOf) val labelPropStatement = step() - have(term is typValue) by Tautology.from( - (argTypesProofs :+ labelPropStatement.of(args: _*) ) : _* + have(term `is` typValue) by Tautology.from( + (argTypesProofs :+ labelPropStatement.of(args*) )* ) typValue diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Frontend.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Frontend.scala index df6d157e3b4d0ce6cc2ed20d0eda85c935942e7d..fa455cd3933c5089e7a6cce74431e08579a87be0 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Frontend.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Frontend.scala @@ -201,7 +201,7 @@ object ADTSyntax { val semanticCons = trimmedNames.tail.zip(syntacticCons).map(SemanticConstructor(_, _, syntacticADT)) val semanticADT = SemanticADT[N](syntacticADT, semanticCons) val cons = semanticCons.map(Constructor(_)) - (ADT[N](semanticADT, cons), new constructors[N](cons : _*)) + (ADT[N](semanticADT, cons), new constructors[N](cons*)) } @@ -356,7 +356,7 @@ object ADTSyntax { * @param adt the ADT * @return a tuple containing the ADT and its constructors */ - private def extractConstructors[N <: Arity](adt: ADT[N]): (ADT[N], constructors[N]) = (adt, constructors(adt.constructors : _*)) + private def extractConstructors[N <: Arity](adt: ADT[N]): (ADT[N], constructors[N]) = (adt, constructors(adt.constructors*)) /** * Outputs a polymorphic ADT and constructors from a user specification @@ -514,7 +514,7 @@ object ADTSyntax { val subst = adtVar -> consTerm val assumptions = - (wellTypedSet(cons.underlying.semanticSignature(vars).map(p => (p._1, p._2.substitute(cons.underlying.typeVariablesSeq.zip(args).map(SubstPair(_, _)) : _*)))) + (wellTypedSet(cons.underlying.semanticSignature(vars).map(p => (p._1, p._2.substitute(cons.underlying.typeVariablesSeq.zip(args).map(SubstPair(_, _))*)))) ++ cons.underlying.syntacticSignature(vars).filter(_._2 == Self).map((v, _) => prop.substitute(adtVar -> v))) diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Helpers.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Helpers.scala index 5830f1aab2e5cdf4acd0c20726ff0bae21e083ad..604b8c34b5d83dc283e26d190aedda62b148174c 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Helpers.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Helpers.scala @@ -265,7 +265,7 @@ private[adt] object ADTDefinitions { def substitute(p: SubstPair*): ConstructorArgument = this match case Self => Self - case GroundType(t) => GroundType(t.substitute(p : _*)) + case GroundType(t) => GroundType(t.substitute(p*)) } /** diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala index cb82507988075654da70cda3dd340e4dab5739cd..7a05b0b49d23ec0c4d17832f26cdadc726aa6216 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Tactics.scala @@ -47,10 +47,10 @@ class Induction[M <: Arity](expectedVar: Option[Variable], expectedADT: Option[A val prop = lambda[Term, Formula](x, propFun(x)) val typeVariablesSubstPairs = adt.typeVariables.toSeq.zip(typeVariablesSubst).map(SubstPair(_, _)) - val instTerm = adt(typeVariablesSubst : _*) + val instTerm = adt(typeVariablesSubst*) - adt.constructors.foldLeft[proof.Fact](adt.induction.of((typeVariablesSubstPairs :+ (P := prop)): _*)) ( (acc, c) => - val inductiveCaseProof = cases(c)._1.zip(c.underlying.underlying.specification.map(_.substitute(typeVariablesSubstPairs : _*))).foldRight[proof.Fact](cases(c)._2) ( (el, acc2) => + adt.constructors.foldLeft[proof.Fact](adt.induction.of((typeVariablesSubstPairs :+ (P := prop))*)) ( (acc, c) => + val inductiveCaseProof = cases(c)._1.zip(c.underlying.underlying.specification.map(_.substitute(typeVariablesSubstPairs*))).foldRight[proof.Fact](cases(c)._2) ( (el, acc2) => val (v, ty) = el val accRight: Formula = acc2.statement.right.head ty match @@ -140,7 +140,7 @@ class Induction[M <: Arity](expectedVar: Option[Variable], expectedADT: Option[A val prop = inferedProp.getOrElse(bot.right.head) val propFunction = (t: Term) => inferedProp.getOrElse(bot.right.head).substitute(inferedVar -> t) - val assignment = inferedVar :: inferedADT(inferedArgs : _*) + val assignment = inferedVar :: inferedADT(inferedArgs*) val context = (if inferedProp.isDefined then bot else bot -<< assignment).left val builder = ADTSyntax.CaseBuilder[N, proof.ProofStep, (Sequent, Seq[Term], Variable)]((context |- prop, inferedArgs, inferedVar)) cases(using builder) diff --git a/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Untyped.scala b/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Untyped.scala index 61dd75a7ebeabdc40fe8f5e3241709afed215374..cb3944c1aad757656068b55a57642db816bf435f 100644 --- a/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Untyped.scala +++ b/lisa-sets/src/main/scala/lisa/maths/settheory/types/adt/Untyped.scala @@ -375,7 +375,7 @@ private class SyntacticADT[N <: Arity](using line: sourcecode.Line, file: source // STEP 2.1: Prove that we can expand the domain of the (quantified) variables of the constructor val andSeq = for (v, ty) <- c.signature2 yield have((subsetST, varsWellTypedS) |- in(v, ty.getOrElse(t))) by Weakening(subsetElimination of (z := v)) - val expandingDomain = have((subsetST, varsWellTypedS) |- varsWellTypedT) by RightAnd(andSeq: _*) + val expandingDomain = have((subsetST, varsWellTypedS) |- varsWellTypedT) by RightAnd(andSeq*) val weakeningLabelEq = have(labelEq |- labelEq) by Hypothesis have((subsetST, varsWellTypedS, labelEq) |- varsWellTypedT /\ labelEq) by RightAnd(expandingDomain, weakeningLabelEq) @@ -390,7 +390,7 @@ private class SyntacticADT[N <: Arity](using line: sourcecode.Line, file: source // STEP 3: Prove that this holds for any constructor // ? Steps 2 and 3 can be merged and optimized through the repeated use of an external theorem like [[ADTHelperTheorems.unionPreimageMonotonic]] if constructors.isEmpty then have((subsetST, isConstructorXS) |- isConstructorXT) by Restate - else have((subsetST, isConstructorXS) |- isConstructorXT) by LeftOr(isConstructorXSImpliesT: _*) + else have((subsetST, isConstructorXS) |- isConstructorXT) by LeftOr(isConstructorXSImpliesT*) // STEP 4: Prove the thesis by showing that making the union with the function argument does not change the monotonicity thenHave(subsetST |- isConstructorXS ==> isConstructorXT) by RightImplies @@ -820,7 +820,7 @@ private class SyntacticADT[N <: Arity](using line: sourcecode.Line, file: source case GroundType(t) => have((hIsTheHeightFunction, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) |- in(v, t)) by Restate - have((hIsTheHeightFunction, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) |- constructorVarsInDomain(c, term)) by RightAnd(andSeq: _*) + have((hIsTheHeightFunction, in(n, N) /\ constructorVarsInDomain(c, app(h, n))) |- constructorVarsInDomain(c, term)) by RightAnd(andSeq*) thenHave((hIsTheHeightFunction, exists(n, in(n, N) /\ constructorVarsInDomain(c, app(h, n)))) |- constructorVarsInDomain(c, term)) by LeftExists } @@ -985,7 +985,7 @@ private class SyntacticADT[N <: Arity](using line: sourcecode.Line, file: source for (v, ty) <- c.signature yield have((hIsTheHeightFunction, in(n, N), constructorVarsInHN) |- in(v, ty.getOrElse(app(h, successor(n))))) by Weakening(liftHeight of (y := v)) - val left = have((hIsTheHeightFunction, in(n, N), constructorVarsInHN) |- constructorVarsInHSuccN) by RightAnd(liftHeightAndSequence: _*) + val left = have((hIsTheHeightFunction, in(n, N), constructorVarsInHN) |- constructorVarsInHSuccN) by RightAnd(liftHeightAndSequence*) val right = have(x === c.term |- x === c.term) by Hypothesis have((hIsTheHeightFunction, in(n, N), constructorVarsInHN, (x === c.term)) |- constructorVarsInHSuccN /\ (x === c.term )) by RightAnd( @@ -998,7 +998,7 @@ private class SyntacticADT[N <: Arity](using line: sourcecode.Line, file: source thenHave((hIsTheHeightFunction, in(n, N), isConstructorCXHN) |- isConstructorXHSuccN) by Weakening - have((hIsTheHeightFunction, in(n, N), isConstructorXHN) |- isConstructorXHSuccN) by LeftOr(liftConstructorHeightOrSequence: _*) + have((hIsTheHeightFunction, in(n, N), isConstructorXHN) |- isConstructorXHSuccN) by LeftOr(liftConstructorHeightOrSequence*) // STEP 1.5: Show that x ∈ introductionFunction(height(n + 1)) => for some c, x = c(x1, ..., xn) // with xi, ..., xj ∈ height(n + 1). @@ -1156,7 +1156,7 @@ private class SyntacticADT[N <: Arity](using line: sourcecode.Line, file: source ).toSeq - have((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN, isConstructor(x, app(h, n))) |- P(x)) by LeftOr(orSeq: _*) + have((hIsTheHeightFunction, structuralInductionPreconditions, in(n, N), inductionFormulaN, isConstructor(x, app(h, n))) |- P(x)) by LeftOr(orSeq*) } // STEP 2.2: Prove that if x ∈ height(n + 1) then P(x) holds. @@ -1734,7 +1734,7 @@ private class SemanticConstructor[N <: Arity](using line: sourcecode.Line, file: have(!inductionPreconditionIneq(c) |- isConstructorMap(c)) by Cut(conditionStrenghtening, lastStep) thenHave(!inductionPreconditionIneq(c) |- isConstructor) by Weakening - have(thesis) by LeftOr(negInductionPreconditionsOrSequence: _*) + have(thesis) by LeftOr(negInductionPreconditionsOrSequence*) } // STEP 2: Conclude diff --git a/lisa-utils/src/main/scala/lisa/fol/Common.scala b/lisa-utils/src/main/scala/lisa/fol/Common.scala index 77977eb2aa151312116cc0dd188ffaf4d995a6f0..3bb97657726158427a1c37aae79e395f86e5f7d8 100644 --- a/lisa-utils/src/main/scala/lisa/fol/Common.scala +++ b/lisa-utils/src/main/scala/lisa/fol/Common.scala @@ -79,7 +79,7 @@ trait Common { * Substitution in the LisaObject of schematics symbols by values. It is not guaranteed by the type system that types of schematics and values match, and the substitution can fail if that is the case. * This is the substitution function that should be implemented. */ - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): T + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): T def substituteUnsafe2[A <: SchematicLabel[?], B <: LisaObject[B]](map: Map[A, B]): T = substituteUnsafe(map.asInstanceOf) /** @@ -87,7 +87,7 @@ trait Common { * This is the substitution that should be used when writing proofs. */ def substitute(pairs: SubstPair*): T = { - substituteUnsafe(Map(pairs.map(s => (s._1, (s._2: LisaObject[_])))*)) + substituteUnsafe(Map(pairs.map(s => (s._1, (s._2: LisaObject[?])))*)) } def substituteOne[S <: LisaObject[S]](v: SchematicLabel[S], arg: S): T = substituteUnsafe(Map(v -> arg)) @@ -219,7 +219,7 @@ trait Common { sealed trait ConstantTermLabel[A <: (Term | (Seq[Term] |-> Term)) & LisaObject[A]] extends TermLabel[A] with ConstantLabel[A] { this: A & LisaObject[A] => val underlyingLabel: K.ConstantFunctionLabel - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): ConstantTermLabel[A] + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): ConstantTermLabel[A] override def rename(newid: Identifier): ConstantTermLabel[A] def freshRename(taken: Iterable[Identifier]): ConstantTermLabel[A] } @@ -276,7 +276,7 @@ trait Common { */ sealed trait FunctionLabel[N <: Arity] extends TermLabel[(Term ** N) |-> Term] with ((Term ** N) |-> Term) { val underlyingLabel: K.TermLabel - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): (Term ** N) |-> Term + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): (Term ** N) |-> Term def applyUnsafe(args: (Term ** N)): Term = AppliedFunctional(this, args.toSeq) override def rename(newid: Identifier): FunctionLabel[N] def freshRename(taken: Iterable[Identifier]): FunctionLabel[N] @@ -293,7 +293,7 @@ trait Common { val underlyingLabel: K.VariableLabel = K.VariableLabel(id) val underlying = K.VariableTerm(underlyingLabel) def applyUnsafe(args: Term ** 0) = this - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): Term = { + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): Term = { map.get(this) match { case Some(subst) => subst match { @@ -344,7 +344,7 @@ trait Common { val underlyingLabel: K.ConstantFunctionLabel = K.ConstantFunctionLabel(id, 0) val underlying = K.Term(underlyingLabel, Seq.empty) def applyUnsafe(args: Term ** 0) = this - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): Constant = this + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): Constant = this def freeSchematicLabels: Set[SchematicLabel[?]] = Set.empty def allSchematicLabels: Set[SchematicLabel[?]] = Set.empty def rename(newid: Identifier): Constant = Constant(newid) @@ -387,7 +387,7 @@ trait Common { case _ => Seq.empty } @nowarn("msg=the type test for.*cannot be checked at runtime because its type arguments") - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): ((Term ** N) |-> Term) = { + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): ((Term ** N) |-> Term) = { map.get(this) match { case Some(subst) => subst match { @@ -441,7 +441,7 @@ trait Common { case AppliedFunctional(label, args) if (label == this) => args case _ => Seq.empty } - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): ConstantFunctionLabel[N] = this + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): ConstantFunctionLabel[N] = this def freeSchematicLabels: Set[SchematicLabel[?]] = Set.empty def allSchematicLabels: Set[SchematicLabel[?]] = Set.empty def rename(newid: Identifier): ConstantFunctionLabel[N] = ConstantFunctionLabel(newid, arity) @@ -486,7 +486,7 @@ trait Common { */ class AppliedFunctional(val label: FunctionLabel[?], val args: Seq[Term]) extends Term with Absolute { override val underlying = K.Term(label.underlyingLabel, args.map(_.underlying)) - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): Term = + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): Term = label.substituteUnsafe(map).applyUnsafe(args.map[Term]((x: Term) => x.substituteUnsafe(map))) def freeSchematicLabels: Set[SchematicLabel[?]] = label.freeSchematicLabels ++ args.flatMap(_.freeSchematicLabels) @@ -567,7 +567,7 @@ trait Common { sealed trait ConstantAtomicLabel[A <: (Formula | (Seq[Term] |-> Formula)) & LisaObject[A]] extends AtomicLabel[A] with ConstantLabel[A] { this: A & LisaObject[A] => val underlyingLabel: K.ConstantAtomicLabel - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): ConstantAtomicLabel[A] + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): ConstantAtomicLabel[A] override def rename(newid: Identifier): ConstantAtomicLabel[A] def freshRename(taken: Iterable[Identifier]): ConstantAtomicLabel[A] } @@ -628,7 +628,7 @@ trait Common { */ sealed trait PredicateLabel[N <: Arity] extends AtomicLabel[(Term ** N) |-> Formula] with ((Term ** N) |-> Formula) with Absolute { val underlyingLabel: K.AtomicLabel - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): (Term ** N) |-> Formula + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): (Term ** N) |-> Formula def applyUnsafe(args: (Term ** N)): Formula = AppliedPredicate(this, args.toSeq) override def rename(newid: Identifier): PredicateLabel[N] def freshRename(taken: Iterable[Identifier]): PredicateLabel[N] @@ -645,7 +645,7 @@ trait Common { val underlyingLabel: K.VariableFormulaLabel = K.VariableFormulaLabel(id) val underlying = K.AtomicFormula(underlyingLabel, Seq.empty) def applyUnsafe(args: Term ** 0): Formula = this - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): Formula = { + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): Formula = { map.get(this) match { case Some(subst) => subst match { @@ -674,7 +674,7 @@ trait Common { val underlyingLabel: K.ConstantAtomicLabel = K.ConstantAtomicLabel(id, 0) val underlying = K.AtomicFormula(underlyingLabel, Seq.empty) def applyUnsafe(args: Term ** 0): Formula = this - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): ConstantFormula = this + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): ConstantFormula = this def freeSchematicLabels: Set[SchematicLabel[?]] = Set.empty def allSchematicLabels: Set[SchematicLabel[?]] = Set.empty def rename(newid: Identifier): ConstantFormula = ConstantFormula(newid) @@ -694,7 +694,7 @@ trait Common { case _ => Seq.empty } @nowarn("msg=the type test for.*cannot be checked at runtime because its type arguments") - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): |->[Term ** N, Formula] = { + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): |->[Term ** N, Formula] = { map.get(this) match { case Some(subst) => subst match { @@ -723,7 +723,7 @@ trait Common { case AppliedPredicate(label, args) if (label == this) => args case _ => Seq.empty } - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): ConstantPredicateLabel[N] = this + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): ConstantPredicateLabel[N] = this def freeSchematicLabels: Set[SchematicLabel[?]] = Set.empty def allSchematicLabels: Set[SchematicLabel[?]] = Set.empty def rename(newid: Identifier): ConstantPredicateLabel[N] = ConstantPredicateLabel(newid, arity) @@ -744,7 +744,7 @@ trait Common { */ case class AppliedPredicate(label: PredicateLabel[?], args: Seq[Term]) extends AtomicFormula with Absolute { override val underlying = K.AtomicFormula(label.underlyingLabel, args.map(_.underlying)) - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): Formula = + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): Formula = label.substituteUnsafe(map).applyUnsafe(args.map[Term]((x: Term) => x.substituteUnsafe(map))) def freeSchematicLabels: Set[SchematicLabel[?]] = label.freeSchematicLabels ++ args.toSeq.flatMap(_.freeSchematicLabels) @@ -769,7 +769,7 @@ trait Common { def applySeq(args: Seq[Formula]): Formula = applyUnsafe(args) def rename(newid: Identifier): ConnectorLabel def freshRename(taken: Iterable[Identifier]): ConnectorLabel - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): |->[Seq[Formula], Formula] + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): |->[Seq[Formula], Formula] def mkString(args: Seq[Formula]): String def mkStringSeparated(args: Seq[Formula]): String @@ -786,7 +786,7 @@ trait Common { case _ => Seq.empty } @nowarn("msg=the type test for.*cannot be checked at runtime because its type arguments") - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): |->[Formula ** N, Formula] = { + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): |->[Formula ** N, Formula] = { map.get(this) match { case Some(subst) => subst match { @@ -820,7 +820,7 @@ trait Common { case AppliedConnector(label, args) if (label == this) => args case _ => Seq.empty } - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): this.type = this + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): this.type = this def applyUnsafe(args: Formula ** N): Formula = AppliedConnector(this, args.toSeq) def freeSchematicLabels: Set[SchematicLabel[?]] = Set.empty def allSchematicLabels: Set[SchematicLabel[?]] = Set.empty @@ -837,7 +837,7 @@ trait Common { */ case class AppliedConnector(label: ConnectorLabel, args: Seq[Formula]) extends Formula with Absolute { override val underlying = K.ConnectorFormula(label.underlyingLabel, args.map(_.underlying)) - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): Formula = + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): Formula = label.applyUnsafe(args.map[Formula]((x: Formula) => x.substituteUnsafe(map))) def freeSchematicLabels: Set[SchematicLabel[?]] = label.freeSchematicLabels ++ args.flatMap(_.freeSchematicLabels) def allSchematicLabels: Set[SchematicLabel[?]] = label.allSchematicLabels ++ args.flatMap(_.allSchematicLabels) @@ -872,7 +872,7 @@ trait Common { } inline def freeSchematicLabels: Set[SchematicLabel[?]] = Set.empty inline def allSchematicLabels: Set[SchematicLabel[?]] = Set.empty - inline def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): this.type = this + inline def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): this.type = this override def toString() = id } @@ -885,7 +885,7 @@ trait Common { def allSchematicLabels: Set[Common.this.SchematicLabel[?]] = body.allSchematicLabels + bound def freeSchematicLabels: Set[Common.this.SchematicLabel[?]] = body.freeSchematicLabels - bound - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): BinderFormula = { + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): BinderFormula = { val newSubst = map - bound if (map.values.flatMap(_.freeSchematicLabels).toSet.contains(bound)) { val taken: Set[SchematicLabel[?]] = body.allSchematicLabels ++ map.keys diff --git a/lisa-utils/src/main/scala/lisa/fol/Lambdas.scala b/lisa-utils/src/main/scala/lisa/fol/Lambdas.scala index bfc1523755fcdc3f1fd961db8924b0e4155141a1..45d1f8689adac8ce7644badcabd0895b82b238aa 100644 --- a/lisa-utils/src/main/scala/lisa/fol/Lambdas.scala +++ b/lisa-utils/src/main/scala/lisa/fol/Lambdas.scala @@ -34,7 +34,7 @@ trait Lambdas extends Common { * @param map * @return */ - def substituteUnsafe(map: Map[SchematicLabel[_], LisaObject[_]]): LambdaExpression[T, R, N] = { + def substituteUnsafe(map: Map[SchematicLabel[?], LisaObject[?]]): LambdaExpression[T, R, N] = { val newSubst = map -- seqBounds val conflict = map.values.flatMap(_.freeSchematicLabels).toSet.intersect(bounds.toSet.asInstanceOf) if (conflict.nonEmpty) { diff --git a/lisa-utils/src/main/scala/lisa/fol/Sequents.scala b/lisa-utils/src/main/scala/lisa/fol/Sequents.scala index 792c75aa199107bbc5f7a6b6fd626f68d47f7350..7c8b4f355c74b36c4b73dc11fe5ab212ea1a47df 100644 --- a/lisa-utils/src/main/scala/lisa/fol/Sequents.scala +++ b/lisa-utils/src/main/scala/lisa/fol/Sequents.scala @@ -30,7 +30,7 @@ trait Sequents extends Common with lisa.fol.Lambdas with Predef { * @param map * @return */ - def instantiateWithProof(map: Map[SchematicLabel[_], _ <: LisaObject[_]], index: Int): (Sequent, Seq[K.SCProofStep]) = { + def instantiateWithProof(map: Map[SchematicLabel[?], ? <: LisaObject[?]], index: Int): (Sequent, Seq[K.SCProofStep]) = { val mTerm: Map[SchematicFunctionLabel[?] | Variable, LambdaExpression[Term, Term, ?]] = map.collect[SchematicFunctionLabel[?] | Variable, LambdaExpression[Term, Term, ?]](p => diff --git a/lisa-utils/src/main/scala/lisa/prooflib/BasicStepTactic.scala b/lisa-utils/src/main/scala/lisa/prooflib/BasicStepTactic.scala index 0501f1b40fb89b60dc9d9677d2f716f32f59bd69..899c51da745ce3e079738e0952626aff68d7f1bc 100644 --- a/lisa-utils/src/main/scala/lisa/prooflib/BasicStepTactic.scala +++ b/lisa-utils/src/main/scala/lisa/prooflib/BasicStepTactic.scala @@ -187,7 +187,7 @@ object BasicStepTactic { else proof.InvalidProofTactic("Right-hand side of conclusion is not a superset of the one of the premises.") } else if (pivots.forall(_.tail.isEmpty)) - LeftOr.withParameters(pivots.map(_.head): _*)(premises: _*)(bot) + LeftOr.withParameters(pivots.map(_.head)*)(premises*)(bot) else // some extraneous formulae proof.InvalidProofTactic("Left-hand side of conclusion + disjuncts is not the same as the union of the left-hand sides of the premises + φ∨ψ.") @@ -586,7 +586,7 @@ object BasicStepTactic { else proof.InvalidProofTactic("Left-hand side of conclusion is not a superset of the one of the premises.") } else if (pivots.forall(_.tail.isEmpty)) - RightAnd.withParameters(pivots.map(_.head): _*)(premises: _*)(bot) + RightAnd.withParameters(pivots.map(_.head)*)(premises*)(bot) else // some extraneous formulae proof.InvalidProofTactic("Right-hand side of conclusion + φ + ψ is not the same as the union of the right-hand sides of the premises +φ∧ψ.") diff --git a/lisa-utils/src/main/scala/lisa/prooflib/ProofsHelpers.scala b/lisa-utils/src/main/scala/lisa/prooflib/ProofsHelpers.scala index ebe992a7996fef41a414cd6d072fbf898a00d135..f6f030f8ed044a9e040d85defc6f69238e9ecf17 100644 --- a/lisa-utils/src/main/scala/lisa/prooflib/ProofsHelpers.scala +++ b/lisa-utils/src/main/scala/lisa/prooflib/ProofsHelpers.scala @@ -120,7 +120,7 @@ trait ProofsHelpers { } extension (using proof: library.Proof)(fact: proof.Fact) { - def of(insts: (F.SubstPair | F.Term)*): proof.InstantiatedFact = { + infix def of(insts: (F.SubstPair | F.Term)*): proof.InstantiatedFact = { proof.InstantiatedFact(fact, insts) } def statement: F.Sequent = proof.sequentOfFact(fact) diff --git a/lisa-utils/src/main/scala/lisa/prooflib/WithTheorems.scala b/lisa-utils/src/main/scala/lisa/prooflib/WithTheorems.scala index fc920eb1b7b8a027f88a4a976eb87e0964599828..628ef7ffa902cdfff00b9198327b0103dcf2f329 100644 --- a/lisa-utils/src/main/scala/lisa/prooflib/WithTheorems.scala +++ b/lisa-utils/src/main/scala/lisa/prooflib/WithTheorems.scala @@ -26,10 +26,7 @@ trait WithTheorems { * * @param assump list of starting assumptions, usually propagated from outer proofs. */ - // TODO: reseal this class - // see https://github.com/lampepfl/dotty/issues/19031 - // and https://github.com/epfl-lara/lisa/issues/190 - abstract class Proof(assump: List[F.Formula]) { + sealed abstract class Proof(assump: List[F.Formula]) { val possibleGoal: Option[F.Sequent] type SelfType = this.type type OutsideFact >: JUSTIFICATION diff --git a/lisa-utils/src/main/scala/lisa/utils/parsing/Parser.scala b/lisa-utils/src/main/scala/lisa/utils/parsing/Parser.scala index eb45a000161d23a55ecacf5c4c62d7fd1587cd7e..73e7af4a1bd06e15a029637be725943085430c02 100644 --- a/lisa-utils/src/main/scala/lisa/utils/parsing/Parser.scala +++ b/lisa-utils/src/main/scala/lisa/utils/parsing/Parser.scala @@ -605,7 +605,7 @@ class Parser( infixPredicateLabels ++ ((and has Associativity.Left) :: (or has Associativity.Left) :: - (toplevelConnector has Associativity.None) :: Nil): _* + (toplevelConnector has Associativity.None) :: Nil)* )( (l, conn, r) => Termula(conn, Seq(l, r), (l.range._1, r.range._2)), { diff --git a/lisa-utils/src/main/scala/lisa/utils/parsing/ParsingUtils.scala b/lisa-utils/src/main/scala/lisa/utils/parsing/ParsingUtils.scala index 6f1565ab117979dd736d43e32c17918685a86793..858f6abffb8426a1c553b8a5ff391075d1242f2b 100644 --- a/lisa-utils/src/main/scala/lisa/utils/parsing/ParsingUtils.scala +++ b/lisa-utils/src/main/scala/lisa/utils/parsing/ParsingUtils.scala @@ -12,7 +12,7 @@ trait ParsingUtils extends Operators { self: Parsers => /** * Indicates the associativity of the operator. */ - def has(associativity: lisa.utils.parsing.Associativity): PrecedenceLevel[Op] = PrecedenceLevel(operator, associativity) + infix def has(associativity: lisa.utils.parsing.Associativity): PrecedenceLevel[Op] = PrecedenceLevel(operator, associativity) } def singleInfix[Op, A](elem: Syntax[A], op: Syntax[Op])(function: (A, Op, A) => A, inverse: PartialFunction[A, (A, Op, A)] = PartialFunction.empty): Syntax[A] =