From baebfcc8da050fe8520b64589aba371cf5700a53 Mon Sep 17 00:00:00 2001 From: Vitaliy <42554566+Leosimetti@users.noreply.github.com> Date: Sat, 11 Jun 2022 18:37:18 +0300 Subject: [PATCH 1/4] Ref(analyzers): fixed variable name --- .../scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala b/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala index 6e4648aa..cf73d58d 100644 --- a/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala +++ b/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala @@ -172,10 +172,10 @@ object EOOdinAnalyzer { ): F[OdinAnalysisResult] = for { programAst <- parser.parse(eoRepr) astWithPredef = addPredef(programAst) - mutualRecursionErrors <- + analysisError <- analyzer .analyze(astWithPredef) // .handleErrorWith(_ => Stream.empty) - } yield mutualRecursionErrors + } yield analysisError } From 90ff89adf54f11d473f547d48980200afaab30e9 Mon Sep 17 00:00:00 2001 From: Vitaliy <42554566+Leosimetti@users.noreply.github.com> Date: Sun, 12 Jun 2022 02:58:53 +0300 Subject: [PATCH 2/4] Feat(analysis)L first version of import support --- .../odin/analysis/EOOdinAnalyzer.scala | 24 +++++- .../analysis/utils/ImportProcessing.scala | 75 +++++++++++++++++++ 2 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 analysis/src/main/scala/org/polystat/odin/analysis/utils/ImportProcessing.scala diff --git a/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala b/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala index cf73d58d..7c651e71 100644 --- a/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala +++ b/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala @@ -1,18 +1,17 @@ package org.polystat.odin.analysis import cats._ -import cats.data.EitherNel -import cats.data.NonEmptyList +import cats.data.{EitherNel, NonEmptyList} import cats.effect.Sync import cats.syntax.all._ import org.polystat.odin.analysis.EOOdinAnalyzer._ import org.polystat.odin.analysis.liskov.Analyzer import org.polystat.odin.analysis.mutualrec.advanced.Analyzer.analyzeAst import org.polystat.odin.analysis.stateaccess.DetectStateAccess +import org.polystat.odin.analysis.utils.ImportProcessing.prependImports import org.polystat.odin.analysis.utils.inlining.Inliner import org.polystat.odin.analysis.utils.j2eo -import org.polystat.odin.core.ast.EOBndExpr -import org.polystat.odin.core.ast.EOProg +import org.polystat.odin.core.ast.{EOBndExpr, EOProg} import org.polystat.odin.core.ast.astparams.EOExprOnly import org.polystat.odin.parser.EoParser import org.polystat.odin.parser.eo.Parser @@ -178,4 +177,21 @@ object EOOdinAnalyzer { // .handleErrorWith(_ => Stream.empty) } yield analysisError + def analyzeSourceCodeDir[EORepr, F[_]]( + analyzer: ASTAnalyzer[F] + )( + fileToEoRepr: Map[String, EORepr] + )(implicit + m: MonadError[F, Throwable], + parser: EoParser[EORepr, F, EOProg[EOExprOnly]], + ): F[Map[String, OdinAnalysisResult]] = for { + + programAsts <- fileToEoRepr.values.toList.traverse(parser.parse) + fileToAst = fileToEoRepr.keys.zip(programAsts).toMap + fileToAstImported <- prependImports[F](fileToAst) + analyzedAsts <- fileToAstImported.values.toList.traverse(analyzer.analyze) + analysisResults = fileToAstImported.keys.zip(analyzedAsts).toMap + + } yield analysisResults + } diff --git a/analysis/src/main/scala/org/polystat/odin/analysis/utils/ImportProcessing.scala b/analysis/src/main/scala/org/polystat/odin/analysis/utils/ImportProcessing.scala new file mode 100644 index 00000000..30da036e --- /dev/null +++ b/analysis/src/main/scala/org/polystat/odin/analysis/utils/ImportProcessing.scala @@ -0,0 +1,75 @@ +package org.polystat.odin.analysis.utils + +import cats.ApplicativeError +import cats.data.NonEmptyList +import cats.syntax.all._ +import higherkindness.droste.data.Fix +import org.polystat.odin.core.ast.astparams.EOExprOnly +import org.polystat.odin.core.ast._ + +object ImportProcessing { + + case class Importable( + src: NonEmptyList[String], + content: EOObj[EOExprOnly] + ) + + def fetchAvailableImportables(src: List[String])( + bnd: EOBndExpr[EOExprOnly] + )(implicit pkg: Option[String]): List[Importable] = Fix.un(bnd.expr) match { + case obj @ EOObj(_, _, bndAttrs) => + val currentName = bnd.bndName.name.name + val newPath = pkg.toList ++ src.appended(currentName) + + List( + Importable(NonEmptyList.fromListUnsafe(newPath), obj) + ) ++ bndAttrs.flatMap(fetchAvailableImportables(newPath)) + case _ => List() + } + + def prependImports[F[_]]( + fileToAst: Map[String, EOProg[EOExprOnly]] + )(implicit + ae: ApplicativeError[F, Throwable] + ): F[Map[String, EOProg[EOExprOnly]]] = { + val usedImports = fileToAst + .toList + .toMap + val availableImports = for { + prog <- fileToAst.values + pkg = prog.metas.pack + importable <- prog + .bnds + .collect { case bnd @ EOBndExpr(_, _) => bnd } flatMap (bnd => + fetchAvailableImportables(List())(bnd)(pkg) + ) + } yield importable + + ae.fromOption( + usedImports + .toList + .traverse { case (fileName, prog @ EOProg(_, _)) => + prog + .metas + .metas + .traverse { + case EOAliasMeta(alias, src) => + for { + imp <- + availableImports + .find(imp => imp.src == src) + boundObj = imp.content + name = alias.getOrElse(src.last) + } yield EOBndExpr(EOAnyNameBnd(LazyName(name)), Fix(boundObj)) + case _ => None + } + .map(importBnds => + (fileName, prog.copy(bnds = prog.bnds.prependedAll(importBnds))) + ) + } + .map(_.toMap), + new Exception("Could not find necessary imports after parsing") + ) + } + +} From 8aa615cbc6fbdf269b1fbdb9d357d0526751ff87 Mon Sep 17 00:00:00 2001 From: Vitaliy <42554566+Leosimetti@users.noreply.github.com> Date: Sun, 12 Jun 2022 16:36:10 +0300 Subject: [PATCH 3/4] Ref(analysis): refactored import-related code --- .../odin/analysis/EOOdinAnalyzer.scala | 6 +- .../analysis/utils/ImportProcessing.scala | 58 +++++++++---------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala b/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala index 7c651e71..5d5f1a9c 100644 --- a/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala +++ b/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala @@ -1,7 +1,8 @@ package org.polystat.odin.analysis import cats._ -import cats.data.{EitherNel, NonEmptyList} +import cats.data.EitherNel +import cats.data.NonEmptyList import cats.effect.Sync import cats.syntax.all._ import org.polystat.odin.analysis.EOOdinAnalyzer._ @@ -11,7 +12,8 @@ import org.polystat.odin.analysis.stateaccess.DetectStateAccess import org.polystat.odin.analysis.utils.ImportProcessing.prependImports import org.polystat.odin.analysis.utils.inlining.Inliner import org.polystat.odin.analysis.utils.j2eo -import org.polystat.odin.core.ast.{EOBndExpr, EOProg} +import org.polystat.odin.core.ast.EOBndExpr +import org.polystat.odin.core.ast.EOProg import org.polystat.odin.core.ast.astparams.EOExprOnly import org.polystat.odin.parser.EoParser import org.polystat.odin.parser.eo.Parser diff --git a/analysis/src/main/scala/org/polystat/odin/analysis/utils/ImportProcessing.scala b/analysis/src/main/scala/org/polystat/odin/analysis/utils/ImportProcessing.scala index 30da036e..f5a42a26 100644 --- a/analysis/src/main/scala/org/polystat/odin/analysis/utils/ImportProcessing.scala +++ b/analysis/src/main/scala/org/polystat/odin/analysis/utils/ImportProcessing.scala @@ -4,27 +4,30 @@ import cats.ApplicativeError import cats.data.NonEmptyList import cats.syntax.all._ import higherkindness.droste.data.Fix -import org.polystat.odin.core.ast.astparams.EOExprOnly import org.polystat.odin.core.ast._ +import org.polystat.odin.core.ast.astparams.EOExprOnly object ImportProcessing { case class Importable( src: NonEmptyList[String], - content: EOObj[EOExprOnly] + content: EOExprOnly ) def fetchAvailableImportables(src: List[String])( bnd: EOBndExpr[EOExprOnly] - )(implicit pkg: Option[String]): List[Importable] = Fix.un(bnd.expr) match { - case obj @ EOObj(_, _, bndAttrs) => - val currentName = bnd.bndName.name.name - val newPath = pkg.toList ++ src.appended(currentName) + )(implicit pkg: Option[String]): List[Importable] = { + val newPath = + NonEmptyList.ofInitLast(src, bnd.bndName.name.name) + val fullPath = pkg.map(newPath.prepend).getOrElse(newPath) - List( - Importable(NonEmptyList.fromListUnsafe(newPath), obj) - ) ++ bndAttrs.flatMap(fetchAvailableImportables(newPath)) - case _ => List() + bnd.expr match { + case obj @ Fix(EOObj(_, _, bndAttrs)) => + List( + Importable(fullPath, obj) + ) ++ bndAttrs.flatMap(fetchAvailableImportables(newPath.toList)) + case other => List(Importable(fullPath, other)) + } } def prependImports[F[_]]( @@ -32,9 +35,6 @@ object ImportProcessing { )(implicit ae: ApplicativeError[F, Throwable] ): F[Map[String, EOProg[EOExprOnly]]] = { - val usedImports = fileToAst - .toList - .toMap val availableImports = for { prog <- fileToAst.values pkg = prog.metas.pack @@ -45,24 +45,24 @@ object ImportProcessing { ) } yield importable + def attemptFetchImport: EOMeta => Option[EOBndExpr[EOExprOnly]] = { + case EOAliasMeta(alias, src) => + for { + imp <- + availableImports + .find(imp => imp.src == src) + boundObj = imp.content + name = alias.getOrElse(src.last) + } yield EOBndExpr(EOAnyNameBnd(LazyName(name)), boundObj) + case _ => None + } + ae.fromOption( - usedImports + fileToAst .toList - .traverse { case (fileName, prog @ EOProg(_, _)) => - prog - .metas - .metas - .traverse { - case EOAliasMeta(alias, src) => - for { - imp <- - availableImports - .find(imp => imp.src == src) - boundObj = imp.content - name = alias.getOrElse(src.last) - } yield EOBndExpr(EOAnyNameBnd(LazyName(name)), Fix(boundObj)) - case _ => None - } + .traverse { case (fileName, prog @ EOProg(EOMetas(_, metas), _)) => + metas + .traverse(attemptFetchImport) .map(importBnds => (fileName, prog.copy(bnds = prog.bnds.prependedAll(importBnds))) ) From ee6c805d076e4876ad2681da4872c9be0366dfb0 Mon Sep 17 00:00:00 2001 From: Vitaliy <42554566+Leosimetti@users.noreply.github.com> Date: Mon, 13 Jun 2022 10:40:58 +0300 Subject: [PATCH 4/4] Feat(analysis.multpleFiles): sharing of top-level objects within a package --- .../analysis/utils/ImportProcessing.scala | 79 +++++++++++++------ 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/analysis/src/main/scala/org/polystat/odin/analysis/utils/ImportProcessing.scala b/analysis/src/main/scala/org/polystat/odin/analysis/utils/ImportProcessing.scala index f5a42a26..1c028f3b 100644 --- a/analysis/src/main/scala/org/polystat/odin/analysis/utils/ImportProcessing.scala +++ b/analysis/src/main/scala/org/polystat/odin/analysis/utils/ImportProcessing.scala @@ -10,24 +10,65 @@ import org.polystat.odin.core.ast.astparams.EOExprOnly object ImportProcessing { case class Importable( - src: NonEmptyList[String], - content: EOExprOnly - ) + pkg: Option[String], + path: NonEmptyList[String], + content: EOExprOnly, + srcFile: String + ) { + val fullSrc: NonEmptyList[String] = pkg.map(path.prepend).getOrElse(path) + val isTopLvl: Boolean = path.length == 1 + + def toBndExpr(alias: Option[String] = None): EOBndExpr[EOExprOnly] = { + val name = alias.getOrElse(path.last) + EOBndExpr(EOAnyNameBnd(LazyName(name)), content) + } + + } def fetchAvailableImportables(src: List[String])( bnd: EOBndExpr[EOExprOnly] - )(implicit pkg: Option[String]): List[Importable] = { + )(implicit pkg: Option[String], sourceFile: String): List[Importable] = { val newPath = NonEmptyList.ofInitLast(src, bnd.bndName.name.name) - val fullPath = pkg.map(newPath.prepend).getOrElse(newPath) bnd.expr match { case obj @ Fix(EOObj(_, _, bndAttrs)) => List( - Importable(fullPath, obj) + Importable(pkg, newPath, obj, sourceFile) ) ++ bndAttrs.flatMap(fetchAvailableImportables(newPath.toList)) - case other => List(Importable(fullPath, other)) + case other => List(Importable(pkg, newPath, other, sourceFile)) + } + } + + def fetchImports( + availableImports: List[Importable] + )(meta: EOMetas): Option[Vector[EOBndExpr[EOExprOnly]]] = { + val currentPkg = meta.pack + + val pkgImports = availableImports.collect { + case imp @ Importable(pkg, _, _, _) + if pkg == currentPkg && imp.isTopLvl => imp.toBndExpr() } + + val nonPkgImports = + meta + .metas + .filter { + // Todo: check a special case where the pkg name eq the import name + case EOAliasMeta(_, src) if !currentPkg.contains(src.head) => true + case _ => false + } + .traverse { + case EOAliasMeta(alias, src) => + for { + imp <- + availableImports + .find(imp => imp.fullSrc == src) + } yield imp.toBndExpr(alias) + case _ => None + } + + nonPkgImports.map(_ ++ pkgImports) } def prependImports[F[_]]( @@ -36,33 +77,19 @@ object ImportProcessing { ae: ApplicativeError[F, Throwable] ): F[Map[String, EOProg[EOExprOnly]]] = { val availableImports = for { - prog <- fileToAst.values + (file, prog) <- fileToAst.toList pkg = prog.metas.pack importable <- prog .bnds - .collect { case bnd @ EOBndExpr(_, _) => bnd } flatMap (bnd => - fetchAvailableImportables(List())(bnd)(pkg) - ) + .collect { case bnd @ EOBndExpr(_, _) => bnd } + .flatMap(bnd => fetchAvailableImportables(List())(bnd)(pkg, file)) } yield importable - def attemptFetchImport: EOMeta => Option[EOBndExpr[EOExprOnly]] = { - case EOAliasMeta(alias, src) => - for { - imp <- - availableImports - .find(imp => imp.src == src) - boundObj = imp.content - name = alias.getOrElse(src.last) - } yield EOBndExpr(EOAnyNameBnd(LazyName(name)), boundObj) - case _ => None - } - ae.fromOption( fileToAst .toList - .traverse { case (fileName, prog @ EOProg(EOMetas(_, metas), _)) => - metas - .traverse(attemptFetchImport) + .traverse { case (fileName, prog @ EOProg(meta, _)) => + fetchImports(availableImports)(meta) .map(importBnds => (fileName, prog.copy(bnds = prog.bnds.prependedAll(importBnds))) )