Skip to content

Commit 8f253eb

Browse files
committed
added integration with py2eo
1 parent 8bf9038 commit 8f253eb

File tree

10 files changed

+175
-112
lines changed

10 files changed

+175
-112
lines changed

.polystat.conf

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@ polystat {
44
outputTo = .
55
tempDir = tmp
66
outputFormats = [sarif]
7-
# excludeRules = [e, c, dialect]
87
}

CHANGELOG.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
1-
## Polystat v0.1.3
1+
## Polystat v0.1.4
22

3-
In this release the `odin` dependency was updated to 0.4.0.
3+
In this release the `py2eo` project was integrated into `polystat-cli`. You can now run:
4+
```
5+
polystat py --in python_files
6+
```
47

5-
The CD pipeline was updated to allow releasing a specified version.
8+
...to analyze a directory with a bunch of python files. For more options and explanations, run:
9+
```
10+
polystat --help
11+
```
12+
or
13+
```
14+
polystat list --config
15+
```
16+
if you want to use the config file.

build.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ libraryDependencies ++= Seq(
2626
"io.circe" %% "circe-core" % "0.14.1",
2727
"org.scalameta" %% "munit" % "1.0.0-M3" % Test,
2828
"org.slf4j" % "slf4j-nop" % "1.7.36",
29+
"org.polystat.py2eo" % "transpiler" % "0.0.10",
2930
)
3031

3132
assembly / assemblyJarName := "polystat.jar"

sandbox_python/test.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def conditionalCheck2():
2+
a = 4
3+
b = 2
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.polystat
2+
3+
import cats.effect.IO
4+
import PolystatConfig.*
5+
import InputUtils.*
6+
import org.polystat.odin.analysis.ASTAnalyzer
7+
import org.polystat.odin.analysis.EOOdinAnalyzer
8+
import org.polystat.odin.parser.EoParser.sourceCodeEoParser
9+
import cats.syntax.traverse.*
10+
import cats.syntax.foldable.*
11+
12+
object EO:
13+
14+
def runAnalyzers(
15+
analyzers: List[ASTAnalyzer[IO]]
16+
)(code: String): IO[List[EOOdinAnalyzer.OdinAnalysisResult]] =
17+
analyzers.traverse(a =>
18+
EOOdinAnalyzer
19+
.analyzeSourceCode(a)(code)(cats.Monad[IO], sourceCodeEoParser[IO]())
20+
)
21+
22+
def analyze(cfg: ProcessedConfig): IO[Unit] =
23+
val inputFiles = readCodeFromInput(".eo", cfg.input)
24+
inputFiles
25+
.evalMap { case (codePath, code) =>
26+
for
27+
_ <- IO.println(s"Analyzing $codePath...")
28+
analyzed <- runAnalyzers(cfg.filteredAnalyzers)(code)
29+
_ <- cfg.output match
30+
case Output.ToConsole => IO.println(analyzed)
31+
case Output.ToDirectory(out) =>
32+
cfg.fmts.traverse_ { case OutputFormat.Sarif =>
33+
val outPath =
34+
out / "sarif" / codePath.replaceExt(".sarif.json")
35+
val sarifJson = SarifOutput(analyzed).json.toString
36+
IO.println(s"Writing results to $outPath") *>
37+
writeOutputTo(outPath)(sarifJson)
38+
}
39+
yield ()
40+
}
41+
.compile
42+
.drain
43+
end analyze
44+
end EO

src/main/scala/org/polystat/InputUtils.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import fs2.text.utf8
99

1010
import java.io.FileNotFoundException
1111

12-
import PolystatConfig.Input
12+
import PolystatConfig.{Input, PolystatUsage}
1313

1414
object InputUtils:
1515
extension (path: Path)
@@ -73,4 +73,22 @@ object InputUtils:
7373
readCodeFromDir(ext = ext, dir = path)
7474
case Input.FromStdin =>
7575
readCodeFromStdin.map(code => (Path("stdin" + ext), "\n" + code + "\n"))
76+
77+
def readConfigFromFile(path: Path): IO[PolystatUsage.Analyze] =
78+
HoconConfig(path).config.load
79+
80+
def writeOutputTo(path: Path)(output: String): IO[Unit] =
81+
for
82+
_ <- path.parent
83+
.map(Files[IO].createDirectories)
84+
.getOrElse(IO.unit)
85+
_ <- Stream
86+
.emits(output.getBytes)
87+
.through(Files[IO].writeAll(path))
88+
.compile
89+
.drain
90+
yield ()
91+
end for
92+
end writeOutputTo
93+
7694
end InputUtils
Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import org.http4s.client.middleware.FollowRedirect
1313
import fs2.io.file.{Path, Files}
1414
import org.http4s.Uri
1515
import sys.process.*
16+
import PolystatConfig.*
17+
import InputUtils.*
1618

17-
object J2EO:
19+
object Java:
1820

1921
private val DEFAULT_J2EO_PATH = Path("j2eo.jar")
2022
private val J2EO_URL =
@@ -53,7 +55,11 @@ object J2EO:
5355
}
5456
end downloadJ2EO
5557

56-
def run(j2eo: Option[Path], inputDir: Path, outputDir: Path): IO[Unit] =
58+
private def runJ2EO(
59+
j2eo: Option[Path],
60+
inputDir: Path,
61+
outputDir: Path,
62+
): IO[Unit] =
5763
val command =
5864
s"java -jar ${j2eo.getOrElse(DEFAULT_J2EO_PATH)} -o $outputDir $inputDir"
5965
for
@@ -69,6 +75,28 @@ object J2EO:
6975
)
7076
yield ()
7177
end for
72-
end run
78+
end runJ2EO
7379

74-
end J2EO
80+
def analyze(j2eo: Option[Path], cfg: ProcessedConfig): IO[Unit] =
81+
for
82+
tmp <- cfg.tempDir
83+
_ <- cfg.input match // writing EO files to tempDir
84+
case Input.FromStdin =>
85+
for
86+
code <- readCodeFromStdin.compile.string
87+
stdinTmp <- Files[IO].createTempDirectory.map(path =>
88+
path / "stdin.eo"
89+
)
90+
_ <- writeOutputTo(stdinTmp)(code)
91+
_ <- runJ2EO(j2eo, inputDir = stdinTmp, outputDir = tmp)
92+
yield ()
93+
case Input.FromFile(path) =>
94+
runJ2EO(j2eo, inputDir = path, outputDir = tmp)
95+
case Input.FromDirectory(path) =>
96+
runJ2EO(j2eo, inputDir = path, outputDir = tmp)
97+
_ <- EO.analyze(
98+
cfg.copy(input = Input.FromDirectory(tmp))
99+
)
100+
yield ()
101+
102+
end Java

src/main/scala/org/polystat/Main.scala

Lines changed: 24 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ import org.polystat.odin.analysis.ASTAnalyzer
1414
import org.polystat.odin.analysis.EOOdinAnalyzer
1515
import org.polystat.odin.analysis.EOOdinAnalyzer.OdinAnalysisResult
1616
import org.polystat.odin.parser.EoParser.sourceCodeEoParser
17+
import org.polystat.py2eo.transpiler.Transpile
1718

1819
import PolystatConfig.*
1920
import IncludeExclude.*
2021
import InputUtils.*
22+
import org.polystat.py2eo.parser.PythonLexer
2123
object Main extends IOApp:
2224
override def run(args: List[String]): IO[ExitCode] =
2325
for exitCode <- CommandIOApp.run(
@@ -49,66 +51,14 @@ object Main extends IOApp:
4951
}
5052
case None => analyzers.map(_._2)
5153

52-
def analyze(
53-
analyzers: List[ASTAnalyzer[IO]]
54-
)(code: String): IO[List[EOOdinAnalyzer.OdinAnalysisResult]] =
55-
analyzers.traverse(a =>
56-
EOOdinAnalyzer
57-
.analyzeSourceCode(a)(code)(cats.Monad[IO], sourceCodeEoParser[IO]())
58-
)
59-
60-
def listAnalyzers: IO[Unit] = analyzers.traverse_ { case (name, _) =>
61-
IO.println(name)
62-
}
63-
64-
def readConfigFromFile(path: Path): IO[PolystatUsage.Analyze] =
65-
HoconConfig(path).config.load
66-
67-
def writeOutputTo(path: Path)(output: String): IO[Unit] =
68-
for
69-
_ <- path.parent
70-
.map(Files[IO].createDirectories)
71-
.getOrElse(IO.unit)
72-
_ <- Stream
73-
.emits(output.getBytes)
74-
.through(Files[IO].writeAll(path))
75-
.compile
76-
.drain
77-
yield ()
78-
end for
79-
end writeOutputTo
80-
81-
def analyzeEO(
82-
inputFiles: Stream[IO, (Path, String)],
83-
outputFormats: List[OutputFormat],
84-
out: Output,
85-
filteredAnalyzers: List[ASTAnalyzer[IO]],
86-
): IO[Unit] =
87-
inputFiles
88-
.evalMap { case (codePath, code) =>
89-
for
90-
_ <- IO.println(s"Analyzing $codePath...")
91-
analyzed <- analyze(filteredAnalyzers)(code)
92-
_ <- out match
93-
case Output.ToConsole => IO.println(analyzed)
94-
case Output.ToDirectory(out) =>
95-
outputFormats.traverse_ { case OutputFormat.Sarif =>
96-
val outPath =
97-
out / "sarif" / codePath.replaceExt(".sarif.json")
98-
val sarifJson = SarifOutput(analyzed).json.toString
99-
IO.println(s"Writing results to $outPath") *>
100-
writeOutputTo(outPath)(sarifJson)
101-
}
102-
yield ()
103-
}
104-
.compile
105-
.drain
106-
10754
def execute(usage: PolystatUsage): IO[Unit] =
10855
usage match
10956
case PolystatUsage.List(cfg) =>
110-
if (cfg) then IO.println(HoconConfig.keys.explanation)
111-
else listAnalyzers
57+
if cfg then IO.println(HoconConfig.keys.explanation)
58+
else
59+
analyzers.traverse_ { case (name, _) =>
60+
IO.println(name)
61+
}
11262
case PolystatUsage.Misc(version, config) =>
11363
if (version) then IO.println(BuildInfo.version)
11464
else
@@ -118,55 +68,26 @@ object Main extends IOApp:
11868
lang,
11969
AnalyzerConfig(inex, input, tmp, fmts, out),
12070
) =>
121-
val filteredAnalyzers = filterAnalyzers(inex)
122-
val tempDir: IO[Path] = tmp match
123-
case Some(path) => IO.pure(path)
124-
case None => Files[IO].createTempDirectory
125-
val inputExt: String = lang match
126-
case SupportedLanguage.EO => ".eo"
127-
case SupportedLanguage.Java(_) => ".java"
128-
case SupportedLanguage.Python => ".py"
129-
71+
val processedConfig = ProcessedConfig(
72+
filteredAnalyzers = filterAnalyzers(inex),
73+
tempDir = tmp match
74+
case Some(path) =>
75+
(IO.println(s"Cleaning ${path.absolute}...") *>
76+
Files[IO].deleteRecursively(path) *>
77+
Files[IO].createDirectory(path))
78+
.as(path)
79+
case None => Files[IO].createTempDirectory
80+
,
81+
output = out,
82+
input = input,
83+
fmts = fmts,
84+
)
13085
val analysisResults: IO[Unit] =
13186
lang match
132-
case SupportedLanguage.EO =>
133-
val inputFiles = readCodeFromInput(ext = inputExt, input = input)
134-
analyzeEO(
135-
inputFiles = inputFiles,
136-
outputFormats = fmts,
137-
out = out,
138-
filteredAnalyzers = filteredAnalyzers,
139-
)
87+
case SupportedLanguage.EO => EO.analyze(processedConfig)
14088
case SupportedLanguage.Java(j2eo) =>
141-
for
142-
tmp <- tempDir
143-
_ <- input match // writing EO files to tempDir
144-
case Input.FromStdin =>
145-
for
146-
code <- readCodeFromStdin.compile.string
147-
stdinTmp <- Files[IO].createTempDirectory.map(path =>
148-
path / "stdin.eo"
149-
)
150-
_ <- writeOutputTo(stdinTmp)(code)
151-
_ <- J2EO.run(j2eo, inputDir = stdinTmp, outputDir = tmp)
152-
yield ()
153-
case Input.FromFile(path) =>
154-
J2EO.run(j2eo, inputDir = path, outputDir = tmp)
155-
case Input.FromDirectory(path) =>
156-
J2EO.run(j2eo, inputDir = path, outputDir = tmp)
157-
inputFiles = readCodeFromInput(
158-
".eo",
159-
Input.FromDirectory(tmp),
160-
)
161-
_ <- analyzeEO(
162-
inputFiles = inputFiles,
163-
outputFormats = fmts,
164-
out = out,
165-
filteredAnalyzers = filteredAnalyzers,
166-
)
167-
yield ()
168-
case SupportedLanguage.Python =>
169-
IO.println("Analyzing Python is not implemented yet!")
89+
Java.analyze(j2eo, processedConfig)
90+
case SupportedLanguage.Python => Python.analyze(processedConfig)
17091
analysisResults
17192
end execute
17293

src/main/scala/org/polystat/PolystatConfig.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import cats.syntax.apply.*
66
import com.monovore.decline.Opts
77
import fs2.Stream
88
import fs2.io.file.Path
9+
import org.polystat.odin.analysis.ASTAnalyzer
910

1011
object PolystatConfig:
1112

@@ -17,6 +18,14 @@ object PolystatConfig:
1718
output: Output,
1819
)
1920

21+
case class ProcessedConfig(
22+
filteredAnalyzers: List[ASTAnalyzer[IO]],
23+
tempDir: IO[Path],
24+
input: Input,
25+
fmts: List[OutputFormat],
26+
output: Output,
27+
)
28+
2029
enum SupportedLanguage:
2130
case EO, Python
2231
case Java(j2eo: Option[Path])
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.polystat
2+
3+
import org.polystat.py2eo.transpiler.Transpile
4+
import fs2.io.file.{Files, Path}
5+
import cats.effect.{IO, IOApp}
6+
import org.polystat.PolystatConfig.*
7+
import org.polystat.InputUtils.*
8+
9+
object Python:
10+
11+
def analyze(cfg: ProcessedConfig): IO[Unit] =
12+
for
13+
tmp <- cfg.tempDir
14+
_ <- readCodeFromInput(".py", cfg.input)
15+
.evalMap { case (path, code) =>
16+
for
17+
maybeCode <- IO.blocking(Transpile(path.toString, code))
18+
_ <- maybeCode match
19+
case Some(code) =>
20+
writeOutputTo(tmp / path.replaceExt(".eo"))(code)
21+
case None => IO.println(s"Couldn't analyze $path...")
22+
yield ()
23+
}
24+
.compile
25+
.drain
26+
_ <- EO.analyze(cfg.copy(input = Input.FromDirectory(tmp)))
27+
yield ()
28+
29+
end Python

0 commit comments

Comments
 (0)