From 1f8fd8a78687bca71f62f6a21a7bbf02c99e114b Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Fri, 20 May 2022 19:18:12 +0300 Subject: [PATCH 1/4] using latest scalafmt --- .scalafmt.conf | 6 +- .../analysis/utils/inlining/Inliner.scala | 400 +++++++++++------- 2 files changed, 242 insertions(+), 164 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 52ac5a13..c41ebd8e 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = 3.4.3 +version = 3.5.4 lineEndings = preserve runner.dialect = scala213 @@ -9,10 +9,6 @@ project { "glob:**.sc", "glob:**.md", ] - excludePaths = [ - // TODO: Investigate why this file causes scalafmt to crash - "glob:**/inlining/Inliner.scala" - ] } fileOverride { diff --git a/analysis/src/main/scala/org/polystat/odin/analysis/utils/inlining/Inliner.scala b/analysis/src/main/scala/org/polystat/odin/analysis/utils/inlining/Inliner.scala index 4b5a118e..c0021a85 100644 --- a/analysis/src/main/scala/org/polystat/odin/analysis/utils/inlining/Inliner.scala +++ b/analysis/src/main/scala/org/polystat/odin/analysis/utils/inlining/Inliner.scala @@ -33,8 +33,13 @@ object Inliner { * 3. Successfully processed 'Object's are converted back into AST-objects * 4. Either the errors or the rebuilt AST are returned */ type PartialObjectTree = ObjectTree[ObjectInfo[ParentName, MethodInfo]] - type CompleteObjectTree = ObjectTree[ObjectInfo[ParentInfo[MethodInfo, ObjectInfo], MethodInfo]] - type ObjectTreeForInlining = ObjectTree[ObjectInfo[ParentInfoForInlining[MethodInfo], MethodInfo]] + + type CompleteObjectTree = + ObjectTree[ObjectInfo[ParentInfo[MethodInfo, ObjectInfo], MethodInfo]] + + type ObjectTreeForInlining = + ObjectTree[ObjectInfo[ParentInfoForInlining[MethodInfo], MethodInfo]] + type ObjectTreeForAnalysis = ObjectTree[ ObjectInfoForAnalysis[ ParentInfo[MethodInfo, ObjectInfoForAnalysis], @@ -45,43 +50,48 @@ object Inliner { def focusObjTreeChildren[ P <: GenericParentInfo, M <: GenericMethodInfo, - O[_ <: GenericParentInfo, _ <: GenericMethodInfo] <: GenericObjectInfo[_, _, O] + O[_ <: GenericParentInfo, _ <: GenericMethodInfo] <: GenericObjectInfo[ + _, + _, + O + ] ]: Lens[ObjectTree[O[P, M]], Map[EONamedBnd, ObjectTree[O[P, M]]]] = GenLens[ObjectTree[O[P, M]]](_.children) - def inlineAllCalls( - prog: EOProg[EOExprOnly] - ): EitherNel[String, EOProg[EOExprOnly]] = { + prog: EOProg[EOExprOnly] + ): EitherNel[String, EOProg[EOExprOnly]] = { type ToplevelBndInfo = ( Vector[Either[EOBnd[EOExprOnly], ObjectPlaceholder]], - Map[EONamedBnd, PartialObjectTree], - ) + Map[EONamedBnd, PartialObjectTree], + ) def parseTopLevelBnds( - bnds: Vector[EOBnd[EOExprOnly]] - ): ToplevelBndInfo - = + bnds: Vector[EOBnd[EOExprOnly]] + ): ToplevelBndInfo = bnds.foldLeft[ToplevelBndInfo]((Vector(), Map())) { case ((bnds, objs), bnd) => parseObject(bnd, 0, List()) match { - case Some(value) => ( - bnds.appended(ObjectPlaceholder(value.info.name).asRight), - objs.updated(value.info.name, value) - ) - case None => ( - bnds.appended(bnd.asLeft), - objs - ) - } + case Some(value) => ( + bnds.appended(ObjectPlaceholder(value.info.name).asRight), + objs.updated(value.info.name, value) + ) + case None => ( + bnds.appended(bnd.asLeft), + objs + ) + } } for { progWithLocators <- setLocators(prog) - (bnds, tree) <- parseTopLevelBnds(progWithLocators.bnds).asRight[Nel[String]] + (bnds, tree) <- + parseTopLevelBnds(progWithLocators.bnds).asRight[Nel[String]] treeWithParents <- resolveParents(tree) - inlinedObjs <- resolveParentsForInlining(treeWithParents).toVector.parTraverse { - case (name, tree) => rebuildObject(tree).map((name, _)) - } + inlinedObjs <- resolveParentsForInlining(treeWithParents) + .toVector + .parTraverse { case (name, tree) => + rebuildObject(tree).map((name, _)) + } .map(_.toMap) inlinedBnds = bnds.map { case Left(bnd) => bnd @@ -91,83 +101,117 @@ object Inliner { } def createObjectTree( - prog: EOProg[EOExprOnly] - ): EitherNel[String, Map[EONamedBnd, PartialObjectTree]] = { + prog: EOProg[EOExprOnly] + ): EitherNel[String, Map[EONamedBnd, PartialObjectTree]] = { setLocators(prog).map( _.bnds .flatMap(bnd => - parseObject(bnd, 0, List()).map(obj => - (obj.info.name, obj) - ) + parseObject(bnd, 0, List()).map(obj => (obj.info.name, obj)) ) .toMap ) } def zipWithInlinedMethod[P <: GenericParentInfo]( - obj: ObjectTree[ObjectInfo[P, MethodInfo]] - ): EitherNel[String, ObjectTree[ObjectInfo[P, MethodInfoAfterInlining]]] = { - obj.traverseMethods[P, MethodInfo, EitherNel[String, *], MethodInfoAfterInlining, ObjectInfo]( - (name, curMethod, curObj) => - inlineCalls(curObj.methods, name).map(bodyAfter => - MethodInfoAfterInlining( - body = curMethod.body, - bodyAfterInlining = bodyAfter - ) + obj: ObjectTree[ObjectInfo[P, MethodInfo]] + ): EitherNel[String, ObjectTree[ObjectInfo[P, MethodInfoAfterInlining]]] = { + obj.traverseMethods[P, MethodInfo, EitherNel[ + String, + * + ], MethodInfoAfterInlining, ObjectInfo]((name, curMethod, curObj) => + inlineCalls(curObj.methods, name).map(bodyAfter => + MethodInfoAfterInlining( + body = curMethod.body, + bodyAfterInlining = bodyAfter ) + ) ) } def resolveParents( - objs: Map[EONamedBnd, PartialObjectTree] - ): EitherNel[String, Map[EONamedBnd, CompleteObjectTree]] = { + objs: Map[EONamedBnd, PartialObjectTree] + ): EitherNel[String, Map[EONamedBnd, CompleteObjectTree]] = { type Context = Map[EONamedBnd, PartialObjectTree] type PathToContext = Optional[Context, Context] - def retrieveParentInfo(ctxs: Nel[PathToContext])(info: Option[ParentName]): EitherNel[String, Option[ParentInfo[MethodInfo, ObjectInfo]]] = { + def retrieveParentInfo(ctxs: Nel[PathToContext])( + info: Option[ParentName] + ): EitherNel[String, Option[ParentInfo[MethodInfo, ObjectInfo]]] = { info match { - case Some(info@ParentName(ObjectNameWithLocator(locator, name))) => + case Some(info @ ParentName(ObjectNameWithLocator(locator, name))) => val names = name.names val pathFromCtxToObj = - names.map(name => optionals.mapValueAtKey[EONamedBnd, PartialObjectTree](EOAnyNameBnd(LazyName(name)))) - .reduceLeft((acc, next) => acc.andThen(focusObjTreeChildren[ParentName, MethodInfo, ObjectInfo]).andThen(next)) - val pathToObject = ctxs.toList.lift(locator.toInt).toRight(Nel.one( - s"Locator overshoot at ${info.toEOName}" - )).map(_.andThen(pathFromCtxToObj)) + names + .map(name => + optionals.mapValueAtKey[EONamedBnd, PartialObjectTree]( + EOAnyNameBnd(LazyName(name)) + ) + ) + .reduceLeft((acc, next) => + acc + .andThen( + focusObjTreeChildren[ParentName, MethodInfo, ObjectInfo] + ) + .andThen(next) + ) + val pathToObject = ctxs + .toList + .lift(locator.toInt) + .toRight( + Nel.one( + s"Locator overshoot at ${info.toEOName}" + ) + ) + .map(_.andThen(pathFromCtxToObj)) for { parentGetter <- pathToObject - _ <- parentGetter.getOption(objs).toRight( - Nel.one(s"There is no such parent with name \"${info.toEOName}\".") + _ <- parentGetter + .getOption(objs) + .toRight( + Nel.one( + s"There is no such parent with name \"${info.toEOName}\"." + ) + ) + } yield Some( + ParentInfo( + linkToParent = parentGetter.asInstanceOf[ + Optional[ + Map[EONamedBnd, CompleteObjectTree], + CompleteObjectTree + ] + ] ) - } yield Some(ParentInfo( - linkToParent = parentGetter.asInstanceOf[ - Optional[ - Map[EONamedBnd, CompleteObjectTree], - CompleteObjectTree] - ] - )) + ) case None => Right(None) } } - def recurse(cur: PartialObjectTree)(ctxs: Nel[PathToContext]): EitherNel[String, CompleteObjectTree] = { + def recurse( + cur: PartialObjectTree + )(ctxs: Nel[PathToContext]): EitherNel[String, CompleteObjectTree] = { val parentName = cur.info.parentInfo ( retrieveParentInfo(ctxs)(parentName), - cur.children.toList.parTraverse { - case (name, subTree) => + cur + .children + .toList + .parTraverse { case (name, subTree) => val ctx = ctxs .head - .andThen(optionals.mapValueAtKey[EONamedBnd, PartialObjectTree](name)) - .andThen(focusObjTreeChildren[ParentName, MethodInfo, ObjectInfo]) + .andThen( + optionals.mapValueAtKey[EONamedBnd, PartialObjectTree](name) + ) + .andThen( + focusObjTreeChildren[ParentName, MethodInfo, ObjectInfo] + ) recurse(subTree)(ctxs.prepend(ctx)).map((name, _)) - }.map(_.toMap) - - ) + } + .map(_.toMap) + ) .mapN((info, children) => ObjectTree( cur.info.copy(parentInfo = info), @@ -176,18 +220,22 @@ object Inliner { ) } - - objs.toList.parTraverse { - case (name, tree) => recurse(tree)( - Nel( - optionals.mapValueAtKey(name).andThen(focusObjTreeChildren), - List(Iso.id) - ) - ).map((name, _)) - }.map(_.toMap) + objs + .toList + .parTraverse { case (name, tree) => + recurse(tree)( + Nel( + optionals.mapValueAtKey(name).andThen(focusObjTreeChildren), + List(Iso.id) + ) + ).map((name, _)) + } + .map(_.toMap) } - def accumulateMethods(fullTree: Map[EONamedBnd, CompleteObjectTree])(cur: CompleteObjectTree): Map[EONamedBnd, MethodInfo] = { + def accumulateMethods( + fullTree: Map[EONamedBnd, CompleteObjectTree] + )(cur: CompleteObjectTree): Map[EONamedBnd, MethodInfo] = { val parent = cur.info.parentInfo.flatMap(_.linkToParent.getOption(fullTree)) val parentMethods: Map[EONamedBnd, MethodInfo] = parent match { case Some(parent) => accumulateMethods(fullTree)(parent) @@ -197,33 +245,46 @@ object Inliner { parentMethods.concat(cur.info.methods) } - def resolveIndirectMethods( - objs: Map[EONamedBnd, CompleteObjectTree] - ): EitherNel[String, Map[EONamedBnd, ObjectTreeForAnalysis]] = { + objs: Map[EONamedBnd, CompleteObjectTree] + ): EitherNel[String, Map[EONamedBnd, ObjectTreeForAnalysis]] = { - - def recurse(cur: CompleteObjectTree): EitherNel[String, ObjectTreeForAnalysis] = { + def recurse( + cur: CompleteObjectTree + ): EitherNel[String, ObjectTreeForAnalysis] = { val allMethods = accumulateMethods(objs)(cur) ( Right(allMethods.removedAll(cur.info.methods.keySet)), - cur.children.toList.parTraverse { - case (name, subTree) => + cur + .children + .toList + .parTraverse { case (name, subTree) => recurse(subTree).map((name, _)) - }.map(_.toMap) - - ) + } + .map(_.toMap) + ) .mapN((methods, children) => ObjectTree( ObjectInfoForAnalysis( name = cur.info.name, methods = cur.info.methods, - parentInfo = cur.info.parentInfo.map(_.asInstanceOf[ParentInfo[MethodInfo, ObjectInfoForAnalysis]]), - indirectMethods = methods.map { - case (name, info) => (name, MethodInfoForAnalysis(body = info.body, depth = info.depth)) + parentInfo = cur + .info + .parentInfo + .map( + _.asInstanceOf[ParentInfo[MethodInfo, ObjectInfoForAnalysis]] + ), + indirectMethods = methods.map { case (name, info) => + ( + name, + MethodInfoForAnalysis(body = info.body, depth = info.depth) + ) }, - allMethods = allMethods.map { - case (name, info) => (name, MethodInfoForAnalysis(body = info.body, depth = info.depth)) + allMethods = allMethods.map { case (name, info) => + ( + name, + MethodInfoForAnalysis(body = info.body, depth = info.depth) + ) } ), children @@ -231,26 +292,35 @@ object Inliner { ) } - - objs.toList.parTraverse { - case (name, tree) => recurse(tree).map((name, _)) - }.map(_.toMap) - + objs + .toList + .parTraverse { case (name, tree) => + recurse(tree).map((name, _)) + } + .map(_.toMap) } - def resolveParentsForInlining(objs: Map[EONamedBnd, CompleteObjectTree]): Map[EONamedBnd, ObjectTreeForInlining] = { - def recurse(currentLevel: Map[EONamedBnd, CompleteObjectTree]): Map[EONamedBnd, ObjectTreeForInlining] = { - currentLevel.fmap { - subTree => - ObjectTree( - info = subTree.info.copy( - parentInfo = subTree.info.parentInfo.map(_ => - ParentInfoForInlining(accumulateMethods(objs)(subTree)) - ) + def resolveParentsForInlining( + objs: Map[EONamedBnd, CompleteObjectTree] + ): Map[EONamedBnd, ObjectTreeForInlining] = { + def recurse( + currentLevel: Map[EONamedBnd, CompleteObjectTree] + ): Map[EONamedBnd, ObjectTreeForInlining] = { + currentLevel.fmap { subTree => + ObjectTree( + info = subTree + .info + .copy( + parentInfo = subTree + .info + .parentInfo + .map(_ => + ParentInfoForInlining(accumulateMethods(objs)(subTree)) + ) ), - children = recurse(subTree.children) - ) + children = recurse(subTree.children) + ) } } @@ -258,9 +328,14 @@ object Inliner { } - type AnalysisInfo = ObjectInfoForAnalysis[ParentInfo[MethodInfo, ObjectInfoForAnalysis], MethodInfo] + type AnalysisInfo = ObjectInfoForAnalysis[ + ParentInfo[MethodInfo, ObjectInfoForAnalysis], + MethodInfo + ] - def zipMethodsWithTheirInlinedVersionsFromParent(prog: EOProg[EOExprOnly]): EitherNel[ + def zipMethodsWithTheirInlinedVersionsFromParent( + prog: EOProg[EOExprOnly] + ): EitherNel[ String, Map[ EONamedBnd, @@ -301,9 +376,9 @@ object Inliner { } def propagateArguments( - methodInfo: MethodInfo, - call: Call, - ): EOObj[EOExprOnly] = { + methodInfo: MethodInfo, + call: Call, + ): EOObj[EOExprOnly] = { val argMap = methodInfo.body.freeAttrs.map(_.name).zip(call.args).toMap val localNames = methodInfo .body @@ -315,8 +390,8 @@ object Inliner { .map(_.bndName.name.name) def getAppReplacementFromArgMap( - currentDepth: BigInt - ): EOExprOnly => Option[EOExprOnly] = { + currentDepth: BigInt + ): EOExprOnly => Option[EOExprOnly] = { case EOSimpleAppWithLocator(name, locator) if locator == currentDepth => argMap .get(name) @@ -325,11 +400,11 @@ object Inliner { } def propagateArguments( - currentDepth: BigInt - ): EOExprOnly => EOExprOnly = { + currentDepth: BigInt + ): EOExprOnly => EOExprOnly = { // It is an argument - case app@Fix(EOSimpleAppWithLocator(name, locator)) - if locator == currentDepth => + case app @ Fix(EOSimpleAppWithLocator(name, locator)) + if locator == currentDepth => argMap .get(name) // Increase all locators in the expression by their depth @@ -342,9 +417,9 @@ object Inliner { .getOrElse(app) // It is some other application - case app@Fix(EOSimpleAppWithLocator(name, locator)) - // Checking that it does not point to a local attribute - if !(localNames.contains(name) && locator == currentDepth) => + case app @ Fix(EOSimpleAppWithLocator(name, locator)) + // Checking that it does not point to a local attribute + if !(localNames.contains(name) && locator == currentDepth) => prisms .fixToEOSimpleAppWithLocator .andThen( @@ -386,9 +461,9 @@ object Inliner { } def processPhi( - phiExpr: EOExpr[EOExprOnly], - attrsObj: Option[EOBndExpr[EOExprOnly]] - ): EOExpr[EOExprOnly] = { + phiExpr: EOExpr[EOExprOnly], + attrsObj: Option[EOBndExpr[EOExprOnly]] + ): EOExpr[EOExprOnly] = { val attrsObjName = attrsObj.map(_.bndName.name.name) val availableBndNames = @@ -399,25 +474,26 @@ object Inliner { def makeAppPointToAttrsObjIfNecessary(depth: BigInt)( app: EOExprOnly ): EOExprOnly = { - val attrMap = attrsObjName.flatMap { objName => { - availableBndNames.map { names => - // Applications that are expected to refer to the attrsObj - val apps = - names.map(name => EOSimpleAppWithLocator(name, depth)) - // Application properly referring to the attrsObj - val appsToAttrsObj = apps.map(app => - EODot( - Fix[EOExpr](EOSimpleAppWithLocator(objName, depth)), - app.name + val attrMap = attrsObjName.flatMap { objName => + { + availableBndNames.map { names => + // Applications that are expected to refer to the attrsObj + val apps = + names.map(name => EOSimpleAppWithLocator(name, depth)) + // Application properly referring to the attrsObj + val appsToAttrsObj = apps.map(app => + EODot( + Fix[EOExpr](EOSimpleAppWithLocator(objName, depth)), + app.name + ) ) - ) - // Making application correspond to the proper reference to attrsObj - apps - .zip(appsToAttrsObj) - .toMap[EOExpr[EOExprOnly], EODot[EOExprOnly]] + // Making application correspond to the proper reference to attrsObj + apps + .zip(appsToAttrsObj) + .toMap[EOExpr[EOExprOnly], EODot[EOExprOnly]] + } } } - } attrMap .flatMap(_.get(Fix.un(app))) @@ -443,9 +519,9 @@ object Inliner { } def inlineCalls( - availableMethods: Map[EONamedBnd, MethodInfo], - methodNameWhereToInline: EONamedBnd - ): EitherNel[String, EOBndExpr[EOExprOnly]] = { + availableMethods: Map[EONamedBnd, MethodInfo], + methodNameWhereToInline: EONamedBnd + ): EitherNel[String, EOBndExpr[EOExprOnly]] = { val methodWhereInliningHappens = availableMethods(methodNameWhereToInline) val methodBody = methodWhereInliningHappens @@ -465,8 +541,8 @@ object Inliner { ) def checkThatTheAmountOfArgsIsCorrect( - methodToInlineInfo: MethodInfo - ): EitherNel[String, Unit] = { + methodToInlineInfo: MethodInfo + ): EitherNel[String, Unit] = { if (methodToInlineInfo.body.freeAttrs.length == call.args.length) Right(()) @@ -478,8 +554,8 @@ object Inliner { } def extractPhiExpr( - methodBody: EOObj[EOExprOnly] - ): EitherNel[String, EOExpr[EOExprOnly]] = + methodBody: EOObj[EOExprOnly] + ): EitherNel[String, EOExpr[EOExprOnly]] = methodBody .bndAttrs .collectFirst { case EOBndExpr(EODecoration, Fix(phiExpr)) => @@ -493,7 +569,7 @@ object Inliner { def getCallsite: EitherNel[String, EOObj[EOExprOnly]] = currentMethodBodyWhereInliningHappens match { - case err@Left(_) => err + case err @ Left(_) => err case Right(body) => call .callSite @@ -508,9 +584,9 @@ object Inliner { } def addAttrsObjToCallSiteIfNecessary( - attrsObj: Option[EOBndExpr[EOExprOnly]], - callSite: EOObj[EOExprOnly] - ): EitherNel[String, EOObj[EOExprOnly]] = { + attrsObj: Option[EOBndExpr[EOExprOnly]], + callSite: EOObj[EOExprOnly] + ): EitherNel[String, EOObj[EOExprOnly]] = { attrsObj .map(localAttrsObj => call @@ -534,8 +610,8 @@ object Inliner { } def extractLocalAttrsObj( - nonPhiBnds: Vector[EOBndExpr[EOExprOnly]] - ): EitherNel[String, Option[EOBndExpr[Fix[EOExpr]]]] = { + nonPhiBnds: Vector[EOBndExpr[EOExprOnly]] + ): EitherNel[String, Option[EOBndExpr[Fix[EOExpr]]]] = { if (nonPhiBnds.nonEmpty) { val attrsObjName = getCallsite.map(callsite => @@ -599,7 +675,9 @@ object Inliner { methodBody.map(body => EOBndExpr(methodNameWhereToInline, Fix(body))) } - def rebuildObject(obj: ObjectTreeForInlining): EitherNel[String, EOBndExpr[EOExprOnly]] = { + def rebuildObject( + obj: ObjectTreeForInlining + ): EitherNel[String, EOBndExpr[EOExprOnly]] = { val newBinds: EitherNel[String, Vector[EOBndExpr[EOExprOnly]]] = obj .info @@ -607,7 +685,12 @@ object Inliner { .parTraverse { case MethodPlaceholder(methodName) => inlineCalls( - obj.info.parentInfo.map(_.parentMethods).getOrElse(Map()).concat(obj.info.methods), + obj + .info + .parentInfo + .map(_.parentMethods) + .getOrElse(Map()) + .concat(obj.info.methods), methodName ) case ObjectPlaceholder(objName) => @@ -615,12 +698,11 @@ object Inliner { .children .get(objName) .toRight( - Nel.one( - s""" - |Object with name ${objName.name.name} - |can not be found directly in ${obj.info.name.name}. - |This is most probably a programming error, report to developers. - |""".stripMargin) + Nel.one(s""" + |Object with name ${objName.name.name} + |can not be found directly in ${obj.info.name.name}. + |This is most probably a programming error, report to developers. + |""".stripMargin) ) .flatMap(rebuildObject) case ParentPlaceholder(expr) => Right(EOBndExpr(EODecoration, expr)) From 73d1195b1ea4612a5e38ab8ec20094b74fd3eb20 Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Fri, 20 May 2022 20:53:53 +0300 Subject: [PATCH 2/4] fixed mutualrec tests --- .../analysis/mutualrec/advanced/Program.scala | 22 +- .../odin/analysis/MutualrecTests.scala | 265 ++---------------- .../gens/MutualRecursionTestGen.scala | 100 +++---- 3 files changed, 70 insertions(+), 317 deletions(-) diff --git a/analysis/src/main/scala/org/polystat/odin/analysis/mutualrec/advanced/Program.scala b/analysis/src/main/scala/org/polystat/odin/analysis/mutualrec/advanced/Program.scala index 05bf5048..f6b082ea 100644 --- a/analysis/src/main/scala/org/polystat/odin/analysis/mutualrec/advanced/Program.scala +++ b/analysis/src/main/scala/org/polystat/odin/analysis/mutualrec/advanced/Program.scala @@ -53,16 +53,20 @@ object Program { def helper(obj: Object, depth: Int): String = { val spaces = " " * (depth + 1) + val locators = List.fill(depth + 1)('^').mkString(".") + val parent = obj + .parent + .fold("")(parent => s"$spaces$locators.${parent.name.show} > @\n") + + val methods = obj + .callGraph + .filter { case (method, _) => + method.whereDefined == obj.name + } + .map(renderMethod(depth + 1)) + .mkString("\n") s"""[] > ${obj.name.name} - |${obj - .parent - .fold("")(parent => s"$spaces${parent.name.show} > @\n")}${obj - .callGraph - .filter { case (method, _) => - method.whereDefined == obj.name - } - .map(renderMethod(depth + 1)) - .mkString("\n")} + |$parent$methods |$spaces${obj .nestedObjs .map(helper(_, depth + 1)) diff --git a/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala b/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala index 39ef219b..2277e2d7 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala @@ -1,9 +1,12 @@ package org.polystat.odin.analysis import cats.data.NonEmptyList +import cats.effect.IO +import cats.effect.Sync import cats.effect.unsafe.implicits.global -import cats.effect.{IO, Sync} -import cats.parse.{Parser => P, Parser0 => P0} +import cats.parse.{Parser => P} +import cats.parse.{Parser0 => P0} +import cats.syntax.foldable._ import cats.syntax.functor._ import fs2.io.file.Files import org.polystat.odin.analysis.gens.MutualRecursionTestGen.genProgram @@ -12,18 +15,21 @@ import org.polystat.odin.analysis.mutualrec.advanced.CallGraph._ import org.polystat.odin.analysis.mutualrec.advanced.Program._ import org.polystat.odin.parser.eo.Parser import org.polystat.odin.utils.files -import org.scalacheck.{Gen, Prop, Test} +import org.scalacheck.Gen +import org.scalacheck.Prop +import org.scalacheck.Test import org.scalatest.Assertion import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.Checkers class MutualrecTests extends AnyWordSpec with Checkers { - val params: Test.Parameters = Test - .Parameters - .default - .withMinSuccessfulTests(1000) - .withWorkers(1) + val params: Test.Parameters = + Test + .Parameters + .default + .withMinSuccessfulTests(1000) + .withWorkers(4) def odinErrors( code: String @@ -39,33 +45,18 @@ class MutualrecTests extends AnyWordSpec with Checkers { "odin" should { "find mutual recursion in auto-generated tests" in { val gen = Gen - .choose(2, 2) + .choose(2, 100) .flatMap(n => genProgram(n).retryUntil(p => p.findMultiObjectCycles.nonEmpty) ) val prop = Prop .forAllNoShrink(gen) { prog => - prog.foreach(_ => ()) -// val code = prog.toEO + "\n" -// val assertion = for { -// errors <- odinErrors(code) -// _ <- Right( -// Try(if (errors.isEmpty) pprintln(prog, height = 10000)) -// .recover(_ => pprintln(prog, height = 10000)) -// ) -// } yield errors.toSet == prog.findMultiObjectCycles.toSet -// -// val finalAssertion = assertion.getOrElse(false) -// if (!finalAssertion) { -// pprintln(prog) -// finalAssertion -// } else -// finalAssertion - - // TODO: Fix tests - true - + val code = prog.toEO + "\n" + val assertion = for { + errors <- odinErrors(code) + } yield errors.toSet == prog.findMultiObjectCycles.toSet + assertion.getOrElse(false) } check(prop, params) } @@ -119,10 +110,7 @@ class MutualrecTests extends AnyWordSpec with Checkers { expectedErrors <- MutualrecTests.parseCallChains(fileNameToChain(fileName)) actualErrors <- odinErrors(code) - } yield { - actualErrors.map(_.show).foreach(println) - actualErrors.toSet == expectedErrors.toSet - } + } yield actualErrors.toSet == expectedErrors.toSet assert(passes.getOrElse(false)) } @@ -164,217 +152,6 @@ class MutualrecTests extends AnyWordSpec with Checkers { object MutualrecTests { - def main(args: Array[String]): Unit = { - val program = List( - Object( - name = ObjectName(names = NonEmptyList(head = "u", tail = List())), - parent = None, - nestedObjs = List(), - callGraph = Map( - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "u", tail = List())), - name = "u" - ) -> Set(), - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "u", tail = List())), - name = "i" - ) -> Set(), - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "u", tail = List())), - name = "t" - ) -> Set(), - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "u", tail = List())), - name = "z" - ) -> Set( - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "u", tail = List())), - name = "u" - ) - ) - ) - ), - Object( - name = ObjectName(names = NonEmptyList(head = "km", tail = List())), - parent = Some( - value = ParentInfo( - name = ObjectName(names = NonEmptyList(head = "u", tail = List())), - callGraph = Map( - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "u", tail = List())), - name = "u" - ) -> Set(), - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "u", tail = List())), - name = "i" - ) -> Set(), - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "u", tail = List())), - name = "t" - ) -> Set(), - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "u", tail = List())), - name = "z" - ) -> Set( - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "u", tail = List())), - name = "u" - ) - ) - ), - parent = None - ) - ), - nestedObjs = List( - Object( - name = - ObjectName(names = NonEmptyList(head = "km", tail = List("jr"))), - parent = None, - nestedObjs = List(), - callGraph = Map() - ), - Object( - name = - ObjectName(names = NonEmptyList(head = "km", tail = List("ie"))), - parent = None, - nestedObjs = List(), - callGraph = Map( - MethodName( - whereDefined = ObjectName(names = - NonEmptyList(head = "km", tail = List("ie")) - ), - name = "z" - ) -> Set(), - MethodName( - whereDefined = ObjectName(names = - NonEmptyList(head = "km", tail = List("ie")) - ), - name = "d" - ) -> Set( - MethodName( - whereDefined = ObjectName(names = - NonEmptyList(head = "km", tail = List("ie")) - ), - name = "z" - ) - ) - ) - ), - Object( - name = - ObjectName(names = NonEmptyList(head = "km", tail = List("w"))), - parent = None, - nestedObjs = List(), - callGraph = Map( - MethodName( - whereDefined = ObjectName(names = - NonEmptyList(head = "km", tail = List("w")) - ), - name = "x" - ) -> Set(), - MethodName( - whereDefined = ObjectName(names = - NonEmptyList(head = "km", tail = List("w")) - ), - name = "l" - ) -> Set() - ) - ) - ), - callGraph = Map( - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "km", tail = List())), - name = "j" - ) -> Set( - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "u", tail = List())), - name = "z" - ) - ), - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "km", tail = List())), - name = "i" - ) -> Set( - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "u", tail = List())), - name = "z" - ) - ), - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "km", tail = List())), - name = "u" - ) -> Set( - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "km", tail = List())), - name = "i" - ) - ), - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "u", tail = List())), - name = "z" - ) -> Set( - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "km", tail = List())), - name = "u" - ) - ), - MethodName( - whereDefined = - ObjectName(names = NonEmptyList(head = "km", tail = List())), - name = "t" - ) -> Set() - ) - ) - ) - - val code = program.toEO + "\r\n" - - val parsed = Parser.parse(code) - - println("before:") - println(code) - program.foreach(obj => { - println(obj.name.show) - println(obj.callGraph.show) - }) - - import cats.syntax.either._ - - println("after:") - parsed - .flatMap(Analyzer.buildObjectTree) - .leftMap(println) - .map(prog => { - println(prog.toEO) - prog.foreach(obj => { - println(obj.name.show) - println(obj.callGraph.show) - }) - }) - .merge - - } - - import cats.syntax.foldable._ - val simpleName: P[String] = P.charsWhile((('a' to 'z') ++ ('A' to 'Z') ++ List('_')).contains(_)) diff --git a/analysis/src/test/scala/org/polystat/odin/analysis/gens/MutualRecursionTestGen.scala b/analysis/src/test/scala/org/polystat/odin/analysis/gens/MutualRecursionTestGen.scala index daafa85b..ea74f834 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/gens/MutualRecursionTestGen.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/gens/MutualRecursionTestGen.scala @@ -1,18 +1,15 @@ package org.polystat.odin.analysis.gens -import cats.effect.IO import cats.effect.kernel.Sync -import cats.effect.unsafe.implicits.global import cats.syntax.flatMap._ import fs2.Stream -import fs2.io.file.{Files, Path} +import fs2.io.file.Files +import fs2.io.file.Path import fs2.text.utf8 +import org.polystat.odin.analysis.ObjectName import org.polystat.odin.analysis.mutualrec.advanced.CallGraph._ import org.polystat.odin.analysis.mutualrec.advanced.Program._ import org.scalacheck.Gen -import org.polystat.odin.analysis.ObjectName - -import scala.util.Try object MutualRecursionTestGen { @@ -38,58 +35,51 @@ object MutualRecursionTestGen { lst <- Gen.listOfN(n, g) } yield lst - def mapRandom[T](lst: List[T])(f: T => Gen[T]): Gen[List[T]] = { - lst match { - case Nil => Gen.const(List.empty) - case last :: Nil => f(last).map(_ :: Nil) - case head :: tail => Gen - .oneOf(true, false) - .flatMap(stop => - if (stop) f(head).map(_ :: tail) - else mapRandom(tail)(f).map(tail => head :: tail) - ) - } + def applyToRandom[T](lst: List[T])(f: T => Gen[T]): Gen[List[T]] = { + if (lst.isEmpty) + Gen.const(List.empty) + else + for { + index <- Gen.choose(0, lst.length - 1) + newElem <- f(lst(index)) + } yield lst.updated(index, newElem) } - def pickOneOrZero[T](lst: List[T]): Gen[Set[T]] = + def pickOneOrZero[T](lst: List[T]): Gen[Option[T]] = for { n <- Gen.oneOf(0, 1) - lst <- Try(Gen.pick(n, lst)).fold( - _ => Gen.const(Set.empty[T]), - gen => gen.map(_.toSet) - ) + lst <- + if (n == 0 || lst.isEmpty) Gen.const(None) + else Gen.pick(n, lst).map(_.headOption) } yield lst def randomlySplit[T](list: List[T]): Gen[(List[T], List[T])] = { - if (list.isEmpty) - Gen.const((List(), List())) - else - for { - part1 <- Gen - .atLeastOne(list) - .map(_.toList) - part2 = list.filter(!part1.contains(_)) - } yield (part1, part2) + list + .foldLeft[Gen[(List[T], List[T])]](Gen.const((List.empty, List.empty))) { + case (acc, next) => + for { + (left, right) <- acc + isLeft <- Gen.oneOf(true, false) + } yield if (isLeft) (left :+ next, right) else (left, right :+ next) + } } - def genMethodName: Gen[String] = + def genMethodName(scope: List[String]): Gen[String] = Gen .listOfN(1, Gen.alphaLowerChar) .map(_.mkString) + .retryUntil(s => !scope.contains(s)) def genCallGraph( methodNamesToDefine: List[MethodName], methodNamesToCall: List[MethodName] ): Gen[CallGraph] = { - for { - calls <- Gen.sequence[CallGraph, CallGraphEntry]( - methodNamesToDefine.map(method => - pickOneOrZero(methodNamesToCall.filter(_ != method)) - .map(calls => (method, calls)) - ) + Gen.sequence[CallGraph, CallGraphEntry]( + methodNamesToDefine.map(method => + pickOneOrZero(methodNamesToCall.filter(_ != method)) + .map(calls => (method, calls.toSet)) ) - - } yield calls + ) } def genExtendedObject( @@ -102,7 +92,7 @@ object MutualRecursionTestGen { // new method definitions newMethodNames <- - between(0, 2, genMethodName) + between(0, 2, genMethodName(List(objName.name))) .retryUntil(names => names.forall(n => !obj.callGraph.containsMethodWithName(n)) ) @@ -138,8 +128,8 @@ object MutualRecursionTestGen { for { acc <- accGen obj <- genObject(acc, Some(containerObj)) - .retryUntil(!_.callGraph.containsSingleObjectCycles) - } yield acc ++ List(obj) + .retryUntil(o => !o.callGraph.containsSingleObjectCycles) + } yield acc :+ obj ) } yield a) ) @@ -152,7 +142,7 @@ object MutualRecursionTestGen { objectName <- genObjectName(scope, containerObj) nestedObjects <- genNestedObjs(objectName) methods <- - between(0, 4, genMethodName) + between(0, 4, genMethodName(List(objectName.name))) .map(_.map(MethodName(objectName, _))) cg <- genCallGraph(methods, methods) } yield Object( @@ -185,7 +175,7 @@ object MutualRecursionTestGen { else genObject(scope, container) ) - .retryUntil(!_.callGraph.containsSingleObjectCycles) + .retryUntil(p => !p.callGraph.containsSingleObjectCycles) } yield scope ++ List(newObj) // Type 1 -> add to topLevel @@ -201,7 +191,7 @@ object MutualRecursionTestGen { currentLevel = container.nestedObjs next <- if (deeper) { - mapRandom(currentLevel)(randomObj => + applyToRandom(currentLevel)(randomObj => addObjRec(randomObj, Some(randomObj.name)) .map(nextLevel => randomObj.copy(nestedObjs = nextLevel)) ).map(objs => objs) @@ -286,22 +276,4 @@ object MutualRecursionTestGen { |""".stripMargin } - - def main(args: Array[String]): Unit = { - generateProgramFiles[IO]( - n = 20, - dir = Path("analysis/src/test/resources/mutualrec/generated"), - programGen = genProgram(3).retryUntil(p => - p.exists(_.callGraph.containsMultiObjectCycles) - ), - converters = List( - (p => textFromProgram(p, "# ", _.toEO), "eo"), -// (p => textFromProgram(p, "// ", _.toCPP), "cpp"), - ) - ) - .compile - .drain - .unsafeRunSync() - } - } From 764ca02f7527bed31e8296bdde99394a2a9582db Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Fri, 20 May 2022 20:55:41 +0300 Subject: [PATCH 3/4] removed unused code --- .../mutualrec/advanced/Analyzer.scala | 371 ++---------------- 1 file changed, 23 insertions(+), 348 deletions(-) diff --git a/analysis/src/main/scala/org/polystat/odin/analysis/mutualrec/advanced/Analyzer.scala b/analysis/src/main/scala/org/polystat/odin/analysis/mutualrec/advanced/Analyzer.scala index cdcb5840..1bd63c58 100644 --- a/analysis/src/main/scala/org/polystat/odin/analysis/mutualrec/advanced/Analyzer.scala +++ b/analysis/src/main/scala/org/polystat/odin/analysis/mutualrec/advanced/Analyzer.scala @@ -1,14 +1,12 @@ package org.polystat.odin.analysis.mutualrec.advanced -import cats.MonadError -import cats.data.{EitherNel, NonEmptyList => Nel} +import cats.ApplicativeError +import cats.data.EitherNel import cats.syntax.apply._ import cats.syntax.either._ -import cats.syntax.flatMap._ import cats.syntax.foldable._ import cats.syntax.functor._ import cats.syntax.traverse._ -import higherkindness.droste.data.Fix import org.polystat.odin.analysis.ObjectName import org.polystat.odin.analysis.mutualrec.advanced.CallGraph._ import org.polystat.odin.analysis.mutualrec.advanced.Program._ @@ -20,28 +18,6 @@ import scala.annotation.tailrec object Analyzer { - case class ObjectInfo( - parent: Option[ObjectName], - methods: Vector[Method], - nestedObjects: Vector[NestedObject] - ) - - type Method = ( - String, // method name - EOObj[EOExprOnly] // method body - ) - - type NestedObject = ( - String, // object name - EOObj[EOExprOnly] // object body - ) - - case class PartialObject( - name: ObjectName, - cg: PartialCallGraph, - parentName: Option[ObjectName], // parent or decoratee - ) - def buildObjectTree( prog: EOProg[EOExprOnly] ): EitherNel[String, Program] = { @@ -165,319 +141,18 @@ object Analyzer { } } - type PartialCall = (Option[ObjectName], String) - type PartialCallGraphEntry = (MethodName, Set[PartialCall]) - type PartialCallGraph = Map[MethodName, Set[PartialCall]] - - final case class Tree[A](node: A, children: List[Tree[A]]) { - - private def untilDefined[B, C](lst: List[B])(f: B => Option[C]): Option[C] = - lst match { - case Nil => None - case head :: tail => f(head).orElse(untilDefined(tail)(f)) - } - - def find(predicate: A => Boolean): Option[A] = - if (predicate(node)) - Some(node) - else - untilDefined(children)(_.find(predicate)) - - } - - def splitObjectBody( - body: Vector[EOBnd[EOExprOnly]] - ): ObjectInfo = - body.foldLeft(ObjectInfo(None, Vector.empty, Vector.empty)) { - case (acc, next) => next match { - - // parent (simple app) - // simple_name > @ - case EOBndExpr( - EODecoration, - Fix(EOSimpleApp(name)) - ) => acc.copy(parent = Some(ObjectName(name))) - - // parent (eo dot) - // a.b.c > @ - case EOBndExpr(EODecoration, Fix(obj: EODot[EOExprOnly])) => - acc.copy(parent = eoDotToObjectName(obj)) - - // object - // [] > objName - // ... - case EOBndExpr( - EOAnyNameBnd(LazyName(name)), - Fix(obj @ EOObj(Vector(), None, _)) - ) => - acc.copy(nestedObjects = acc.nestedObjects.appended((name, obj))) - - // method - // [self params] > methodName - // ... - case EOBndExpr( - EOAnyNameBnd(LazyName(name)), - Fix( - obj @ EOObj(_ @LazyName("self") +: _, _, _) - ) - ) => acc.copy(methods = - acc.methods.appended((name, obj)) - ) - - // any other binding that is not one of the above - // 2 > two - // 2.add 2 > four - // etc. - case _ => acc - } - } - - def eoDotToObjectName(eoDot: EODot[EOExprOnly]): Option[ObjectName] = - eoDot match { - case EODot(EOSimpleApp(obj), attr) => - Some(ObjectName(Nel(obj, List(attr)))) - case EODot(Fix(dot: EODot[EOExprOnly]), name) => eoDotToObjectName(dot) - .map(container => ObjectName(container.names.append(name))) - case _ => None - } - - def extractCalls[F[_]]( - body: Vector[EOExpr[EOExprOnly]] - )( - getContainer: String => Option[ObjectName] - )(implicit F: MonadError[F, String]): F[Set[PartialCall]] = - body.foldLeft[F[Set[PartialCall]]](F.pure(Set.empty)) { case (acc, next) => - next match { - case EOCopy( - Fix(EODot(Fix(EOSimpleApp("self")), name)), - args - ) => - for { - accWithMethod <- args - .headOption - .map(bnd => Fix.un(bnd.expr)) - .fold(acc) { - case EOSimpleApp("self") => - acc.map(_ + ((getContainer(name), name))) - case _ => acc - } - callsFromArgs <- extractCalls( - args.tail.map(bnd => Fix.un(bnd.expr)) - )(getContainer) - } yield accWithMethod.union(callsFromArgs) - - case EOCopy(Fix(trg), args) => - for { - acc <- acc - callsFromTrg <- extractCalls(Vector(trg))(getContainer) - callsFromArgs <- extractCalls( - args.value.map(bnd => Fix.un(bnd.expr)) - )(getContainer) - - } yield acc.union(callsFromTrg).union(callsFromArgs) - case EODot(Fix(trg), _) => - for { - acc <- acc - callsFromTrg <- extractCalls(Vector(trg))(getContainer) - } yield acc.union(callsFromTrg) - case _ => acc - } - } - - def extractCallGraph[F[_]: MonadError[*[_], String]]( - container: ObjectName - )(methods: Vector[Method]): F[PartialCallGraph] = { - def extractCallGraphEntry( - method: Method - ): F[PartialCallGraphEntry] = { - val (methodName, methodBody) = method - for { - calls <- extractCalls( - methodBody.bndAttrs.map(bnd => Fix.un(bnd.expr)) - ) { name => methods.find(_._1 == name).map(_ => container) } - } yield ( - MethodName(container, methodName), - calls - ) - } - methods.traverse(extractCallGraphEntry).map(_.toMap) - } - - def buildTreeFromObj[F[_]: MonadError[*[_], String]]( - container: Option[ObjectName] - )( - obj: NestedObject - ): F[Tree[PartialObject]] = { - - val (name, body) = obj - val bodyInfo = splitObjectBody(body.bndAttrs) - val objectName = ObjectName.fromContainer(container, name) - - for { - cg <- extractCallGraph(objectName)(bodyInfo.methods) - nestedObjs <- - bodyInfo.nestedObjects.traverse(buildTreeFromObj[F](Some(objectName))) - } yield Tree( - node = - PartialObject( - name = objectName, - parentName = bodyInfo.parent, - cg = cg - ), - children = nestedObjs.toList - ) - } - - def buildTree[F[_]: MonadError[*[_], String]]( - prog: EOProg[EOExprOnly] - ): F[Vector[Tree[PartialObject]]] = - splitObjectBody(prog.bnds) - .nestedObjects - .traverse(buildTreeFromObj[F](None)) - - def resolveParent[F[_]]( - progTree: Tree[PartialObject] // only for lookup - )( - of: ObjectName, /* object whose parent is being resolved, used for error - * reporting */ - maybeParentName: Option[ObjectName], // parent object name - )(implicit F: MonadError[F, String]): F[Option[ParentInfo]] = { - // returns: - // F.pure(None) - parent was not found and should not be found - // F.raiseError(...) - some exceptional case - // F.pure(Some(...)) - parent was found - maybeParentName match { - case Some(parentName) => - progTree - .find(_.name == parentName) - .fold[F[Option[ParentInfo]]]( - F.raiseError( - s"Parent (or decoratee) object \"${parentName.show}\" of object \"${of.show}\" " + - s"is specified, but not defined in the program!" - ) - ) { - case PartialObject(parentObjName, parentCg, maybeParentOfParent) => - for { - parentOfParent <- - resolveParent(progTree)(parentObjName, maybeParentOfParent) - cg <- convertPartialCg(parentObjName, parentCg, parentOfParent) - } yield Some( - ParentInfo( - name = parentObjName, - callGraph = - parentOfParent.fold(cg)(_.callGraph.extendWith(cg)), - parent = parentOfParent - ) - ) - } - case None => F.pure(None) - } - } - - def convertPartialCg[F[_]]( - objectName: ObjectName, /* where call graph is defined, used for error - * reporting */ - pcg: PartialCallGraph, // call graph to resolve - maybeParent: Option[ParentInfo] /* the parent object to resolve methods from - * parent obj */ - )(implicit F: MonadError[F, String]): F[CallGraph] = { - - def createErrorMsg(methodName: String): String = { - s"Method \"$methodName\" was called from the object \"${objectName.show}\"," + - s" although it is not defined there!" - } - - def resolveCallWithParent( - parent: ParentInfo - )(pc: PartialCall): F[MethodName] = { - pc match { - case (Some(whereDefined), name) => - F.pure(MethodName(whereDefined, name)) - case (None, name) => - parent - .callGraph - .keySet - .find(_.name == name) - .fold[F[MethodName]](parent.parent match { - case Some(parentOfParent) => parent - .callGraph - .keySet - .find(_.name == name) - .fold[F[MethodName]]( - resolveCallWithParent(parentOfParent)(pc) - )(F.pure) - case None => F.raiseError(createErrorMsg(name)) - })(F.pure) - } - } - - def resolveCallNoParent(pc: PartialCall): F[MethodName] = pc match { - case (Some(whereDefined), name) => - F.pure(MethodName(whereDefined, name)) - case (None, name) => F.raiseError(createErrorMsg(name)) - } - - val resolveCall: PartialCall => F[MethodName] = maybeParent match { - case Some(parent) => resolveCallWithParent(parent) - case None => resolveCallNoParent - } - - pcg - .toList - .traverse { case (methodName, partialCalls) => - partialCalls - .toList - .traverse(resolveCall) - .map(calls => (methodName, calls.toSet)) - } - .map(_.toMap) - } - - def restoreObjectFromTree[F[_]]( - progTree: Tree[PartialObject] // only for lookup - )(tree: Tree[PartialObject])(implicit F: MonadError[F, String]): F[Object] = { - val PartialObject(partialObjName, pcg, maybeParent) = tree.node - for { - parent <- resolveParent[F](progTree)(partialObjName, maybeParent) - cg <- convertPartialCg[F](partialObjName, pcg, parent) - nestedObjs <- tree.children.traverse(restoreObjectFromTree[F](progTree)) - } yield Object( - name = partialObjName, - parent = parent, - callGraph = parent.fold(cg)(_.callGraph.extendWith(cg)), - nestedObjs = nestedObjs - ) - } - - def buildProgram[F[_]]( - trees: Vector[Tree[PartialObject]] - )(implicit F: MonadError[F, String]): F[Program] = { - - val dummyObj: PartialObject = PartialObject( - name = ObjectName("THIS NAME DOESN'T MATTER"), - parentName = None, - cg = Map(), - ) - - trees - .traverse(restoreObjectFromTree[F](Tree(dummyObj, trees.toList))) - .map(objs => objs.toList) - } - - private def fromEitherNel[F[_]: MonadError[*[_], String], A]( + private def fromEitherNel[F[_], A]( einel: EitherNel[String, A] - ): F[A] = { + )(implicit F: ApplicativeError[F, String]): F[A] = { val either: Either[String, A] = einel.leftMap(_.mkString_(util.Properties.lineSeparator)) - MonadError[F, String].fromEither(either) + F.fromEither(either) } - private[analysis] def produceChains[F[_]: MonadError[*[_], String]]( + private[analysis] def produceChains[F[_]]( prog: EOProg[EOExprOnly] - ): F[List[CallChain]] = - for { - program <- fromEitherNel(buildObjectTree(prog)) - } yield program.findMultiObjectCycles + )(implicit F: ApplicativeError[F, String]): F[List[CallChain]] = + fromEitherNel(buildObjectTree(prog)).map(_.findMultiObjectCycles) def filterCycleShifts( ccs: List[(ObjectName, CallChain)] @@ -496,21 +171,21 @@ object Analyzer { def analyzeAst[F[_]]( prog: EOProg[EOExprOnly] - )(implicit F: MonadError[F, String]): F[List[String]] = - for { - program <- fromEitherNel(buildObjectTree(prog)) - } yield filterCycleShifts(program.findMultiObjectCyclesWithObject).map { - case (objName, ccs) => - val fancyChain = ccs - .map(methodName => - if (objName == methodName.whereDefined) methodName.show - else methodName - .copy(whereDefined = objName) - .show + s" (was last redefined in \"${methodName.whereDefined.show}\")" - ) - .mkString(" -> ") + )(implicit F: ApplicativeError[F, String]): F[List[String]] = + fromEitherNel(buildObjectTree(prog)).map(program => + filterCycleShifts(program.findMultiObjectCyclesWithObject).map { + case (objName, ccs) => + val fancyChain = ccs + .map(methodName => + if (objName == methodName.whereDefined) methodName.show + else methodName + .copy(whereDefined = objName) + .show + s" (was last redefined in \"${methodName.whereDefined.show}\")" + ) + .mkString(" -> ") - s"${objName.show}: $fancyChain" - } + s"${objName.show}: $fancyChain" + } + ) } From c9b6e7f70c25f05ae52074a32ba20cbe60ab7a33 Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Fri, 20 May 2022 21:15:28 +0300 Subject: [PATCH 4/4] scalafmt --- .../gens/MutualRecursionTestGen.scala | 1 + project/Compiler.scala | 51 ++++++++++++------- .../org/polystat/odin/sandbox/programs.scala | 26 +++++++--- 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/analysis/src/test/scala/org/polystat/odin/analysis/gens/MutualRecursionTestGen.scala b/analysis/src/test/scala/org/polystat/odin/analysis/gens/MutualRecursionTestGen.scala index ea74f834..35b02887 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/gens/MutualRecursionTestGen.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/gens/MutualRecursionTestGen.scala @@ -276,4 +276,5 @@ object MutualRecursionTestGen { |""".stripMargin } + } diff --git a/project/Compiler.scala b/project/Compiler.scala index e38f1d27..47588320 100644 --- a/project/Compiler.scala +++ b/project/Compiler.scala @@ -11,32 +11,48 @@ object Compiler { "-feature", /* Emit warning and location for usages of features that should be imported * explicitly. */ - "-language:existentials", // Existential types (besides wildcard types) can be written and inferred - "-language:experimental.macros", // Allow macro definition (besides implementation and application) + "-language:existentials", /* Existential types (besides wildcard types) can + * be written and inferred */ + "-language:experimental.macros", /* Allow macro definition (besides + * implementation and application) */ "-language:higherKinds", // Allow higher-kinded types - "-language:implicitConversions", // Allow definition of implicit functions called views - "-unchecked", // Enable additional warnings where generated code depends on assumptions. - "-Xcheckinit", // Wrap field accessors to throw an exception on uninitialized access. + "-language:implicitConversions", /* Allow definition of implicit functions + * called views */ + "-unchecked", /* Enable additional warnings where generated code depends on + * assumptions. */ + "-Xcheckinit", /* Wrap field accessors to throw an exception on + * uninitialized access. */ "-Xfatal-warnings", // Fail the compilation if there are any warnings. // "-Xfuture", // Turn on future language features. - "-Xlint:adapted-args", // Warn if an argument list is modified to match the receiver. - "-Xlint:constant", // Evaluation of a constant arithmetic expression results in an error. + "-Xlint:adapted-args", /* Warn if an argument list is modified to match the + * receiver. */ + "-Xlint:constant", /* Evaluation of a constant arithmetic expression results + * in an error. */ "-Xlint:delayedinit-select", // Selecting member of DelayedInit. - "-Xlint:doc-detached", // A Scaladoc comment appears to be detached from its element. - "-Xlint:inaccessible", // Warn about inaccessible types in method signatures. + "-Xlint:doc-detached", /* A Scaladoc comment appears to be detached from its + * element. */ + "-Xlint:inaccessible", /* Warn about inaccessible types in method + * signatures. */ "-Xlint:infer-any", // Warn when a type argument is inferred to be `Any`. - "-Xlint:missing-interpolator", // A string literal appears to be missing an interpolator id. + "-Xlint:missing-interpolator", /* A string literal appears to be missing an + * interpolator id. */ "-Xlint:nullary-unit", // Warn when nullary methods return Unit. "-Xlint:option-implicit", // Option.apply used implicit view. - "-Xlint:package-object-classes", // Class or object defined in package object. - "-Xlint:poly-implicit-overload", // Parameterized overloaded implicit methods are not visible as view bounds. - "-Xlint:private-shadow", // A private field (or class parameter) shadows a superclass field. - "-Xlint:stars-align", // Pattern sequence wildcard must align with sequence component. - "-Xlint:type-parameter-shadow", // A local type parameter shadows a type already in scope. + "-Xlint:package-object-classes", /* Class or object defined in package + * object. */ + "-Xlint:poly-implicit-overload", /* Parameterized overloaded implicit + * methods are not visible as view bounds. */ + "-Xlint:private-shadow", /* A private field (or class parameter) shadows a + * superclass field. */ + "-Xlint:stars-align", /* Pattern sequence wildcard must align with sequence + * component. */ + "-Xlint:type-parameter-shadow", /* A local type parameter shadows a type + * already in scope. */ "-Xlint:adapted-args", /* Do not adapt an argument list (either by inserting () or creating a * tuple) to match the receiver. */ - "-Xlint:inaccessible", // Warn about inaccessible types in method signatures. + "-Xlint:inaccessible", /* Warn about inaccessible types in method + * signatures. */ "-Xlint:infer-any", // Warn when a type argument is inferred to be `Any`. "-Xlint:nullary-unit", // Warn when nullary methods return Unit. // "-Xlint:nullary-override", // Warn when non-nullary `def f()' overrides @@ -47,7 +63,8 @@ object Compiler { // "-Ypartial-unification", // Enable partial unification in type // constructor inference "-Ywarn-dead-code", // Warn when dead code is identified. - "-Ywarn-extra-implicit", // Warn when more than one implicit parameter section is defined. + "-Ywarn-extra-implicit", /* Warn when more than one implicit parameter + * section is defined. */ // "-Ywarn-nullary-override", // Warn when non-nullary `def f()' overrides // nullary `def f'. "-Ywarn-numeric-widen", // Warn when numerics are widened. diff --git a/sandbox/src/main/scala/org/polystat/odin/sandbox/programs.scala b/sandbox/src/main/scala/org/polystat/odin/sandbox/programs.scala index 53f699fc..37e01778 100644 --- a/sandbox/src/main/scala/org/polystat/odin/sandbox/programs.scala +++ b/sandbox/src/main/scala/org/polystat/odin/sandbox/programs.scala @@ -7,12 +7,19 @@ import org.polystat.odin.core.ast.astparams.EOExprOnly import org.polystat.odin.core.ast._ object programs { + val mutualRecursionExample: EOProg[EOExprOnly] = EOProg( EOMetas( pack = Some("sandbox"), metas = Vector( - EOAliasMeta(Some("stdout"), NonEmptyList("org", List("eolang", "io", "stdout"))), - EOAliasMeta(Some("sprintf"), NonEmptyList("org", List("eolang", "txt", "sprintf"))), + EOAliasMeta( + Some("stdout"), + NonEmptyList("org", List("eolang", "io", "stdout")) + ), + EOAliasMeta( + Some("sprintf"), + NonEmptyList("org", List("eolang", "txt", "sprintf")) + ), ) ), Vector( @@ -38,7 +45,9 @@ object programs { EODecoration, Fix[EOExpr]( EOCopy( - Fix[EOExpr](EODot(Fix[EOExpr](EOSimpleApp("x")), "write")), + Fix[EOExpr]( + EODot(Fix[EOExpr](EOSimpleApp("x")), "write") + ), NonEmpty[Vector[EOBnd[EOExprOnly]]]( EOAnonExpr(Fix[EOExpr](EOSimpleApp("v"))) ) @@ -60,7 +69,9 @@ object programs { EODecoration, Fix[EOExpr]( EOCopy( - Fix[EOExpr](EODot(Fix[EOExpr](EOSimpleApp("self")), "f")), + Fix[EOExpr]( + EODot(Fix[EOExpr](EOSimpleApp("self")), "f") + ), NonEmpty[Vector[EOBnd[EOExprOnly]]]( EOAnonExpr(Fix[EOExpr](EOSimpleApp("self"))), EOAnonExpr(Fix[EOExpr](EOSimpleApp("v"))) @@ -76,8 +87,6 @@ object programs { ) ) ), - - EOBndExpr( EOAnyNameBnd(LazyName("derived")), Fix[EOExpr]( @@ -97,7 +106,9 @@ object programs { EODecoration, Fix[EOExpr]( EOCopy( - Fix[EOExpr](EODot(Fix[EOExpr](EOSimpleApp("self")), "g")), + Fix[EOExpr]( + EODot(Fix[EOExpr](EOSimpleApp("self")), "g") + ), NonEmpty[Vector[EOBnd[EOExprOnly]]]( EOAnonExpr(Fix[EOExpr](EOSimpleApp("self"))), EOAnonExpr(Fix[EOExpr](EOSimpleApp("v"))) @@ -115,4 +126,5 @@ object programs { ), ) ) + }