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..5d5f1a9c 100644 --- a/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala +++ b/analysis/src/main/scala/org/polystat/odin/analysis/EOOdinAnalyzer.scala @@ -9,6 +9,7 @@ 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 @@ -172,10 +173,27 @@ object EOOdinAnalyzer { ): F[OdinAnalysisResult] = for { programAst <- parser.parse(eoRepr) astWithPredef = addPredef(programAst) - mutualRecursionErrors <- + analysisError <- analyzer .analyze(astWithPredef) // .handleErrorWith(_ => Stream.empty) - } yield mutualRecursionErrors + } 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..1c028f3b --- /dev/null +++ b/analysis/src/main/scala/org/polystat/odin/analysis/utils/ImportProcessing.scala @@ -0,0 +1,102 @@ +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._ +import org.polystat.odin.core.ast.astparams.EOExprOnly + +object ImportProcessing { + + case class Importable( + 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], sourceFile: String): List[Importable] = { + val newPath = + NonEmptyList.ofInitLast(src, bnd.bndName.name.name) + + bnd.expr match { + case obj @ Fix(EOObj(_, _, bndAttrs)) => + List( + Importable(pkg, newPath, obj, sourceFile) + ) ++ bndAttrs.flatMap(fetchAvailableImportables(newPath.toList)) + 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[_]]( + fileToAst: Map[String, EOProg[EOExprOnly]] + )(implicit + ae: ApplicativeError[F, Throwable] + ): F[Map[String, EOProg[EOExprOnly]]] = { + val availableImports = for { + (file, prog) <- fileToAst.toList + pkg = prog.metas.pack + importable <- prog + .bnds + .collect { case bnd @ EOBndExpr(_, _) => bnd } + .flatMap(bnd => fetchAvailableImportables(List())(bnd)(pkg, file)) + } yield importable + + ae.fromOption( + fileToAst + .toList + .traverse { case (fileName, prog @ EOProg(meta, _)) => + fetchImports(availableImports)(meta) + .map(importBnds => + (fileName, prog.copy(bnds = prog.bnds.prependedAll(importBnds))) + ) + } + .map(_.toMap), + new Exception("Could not find necessary imports after parsing") + ) + } + +}