Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compiler): Add warnings subsystem [fixes LNG-117] #906

Merged
merged 33 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7f6ded7
ErrorsAlgebra -> ReportAlgebra
InversionSpaces Sep 20, 2023
dac15b1
Refactor ReportAlgebra
InversionSpaces Sep 20, 2023
9673fc5
Refactor
InversionSpaces Sep 20, 2023
d2ebae7
Refactor AquaError
InversionSpaces Sep 20, 2023
7965d55
Fixes
InversionSpaces Sep 20, 2023
d1d5f60
Add warnings, refactor
InversionSpaces Sep 20, 2023
e2323c2
Refactor parser
InversionSpaces Sep 20, 2023
61042ee
Move semantics
InversionSpaces Sep 20, 2023
e10bd62
Savepoint
InversionSpaces Sep 20, 2023
da8667f
Refactor semantics and compiler
InversionSpaces Sep 21, 2023
178c538
Refactor types
InversionSpaces Sep 21, 2023
19b7ee3
Refactor compiler
InversionSpaces Sep 21, 2023
52ac7ce
Merge branch 'main' into feat/warnings-LNG117
InversionSpaces Sep 21, 2023
2e0fa13
Refactor compiler
InversionSpaces Sep 21, 2023
28d8d48
Refactor types
InversionSpaces Sep 21, 2023
d936fd5
Refactor retunr types
InversionSpaces Sep 22, 2023
76b2e2b
Return warnings
InversionSpaces Sep 22, 2023
aad5b66
Add simple warning
InversionSpaces Sep 22, 2023
5c0de8d
Refactor to ValidatedNec
InversionSpaces Sep 22, 2023
d4e6b3d
Refactor
InversionSpaces Sep 22, 2023
5ed154a
Add comment
InversionSpaces Sep 22, 2023
7a79007
Propagate warnings to LspContext
InversionSpaces Sep 22, 2023
4b3cde0
Propagate warnings to LSP
InversionSpaces Sep 25, 2023
6406cc7
Add warnings to js api
InversionSpaces Sep 25, 2023
0a89df6
Update LSP js api
InversionSpaces Sep 25, 2023
16d14f0
Use export declare
InversionSpaces Sep 25, 2023
cf5e643
Add comment
InversionSpaces Sep 25, 2023
e1be1a3
Merge branch 'main' into feat/warnings-LNG117
InversionSpaces Sep 25, 2023
cb081c8
Refactor span rendering
InversionSpaces Sep 25, 2023
a2e3f59
Remove variable name warning
InversionSpaces Sep 25, 2023
eded8e8
Add warning on unused call results
InversionSpaces Sep 25, 2023
3277274
Add unit tests
InversionSpaces Sep 25, 2023
98e8258
Remove println
InversionSpaces Sep 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions api/api-npm/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import { ServiceDef, FunctionCallDef } from "@fluencelabs/interfaces";

export class AquaFunction {
export declare class AquaFunction {
funcDef: FunctionCallDef;
script: string;
}

export class GeneratedSource {
export declare class GeneratedSource {
name: string;
tsSource?: string;
jsSource?: string;
tsTypes?: string;
}

class CompilationResult {
export declare class CompilationResult {
services: Record<string, ServiceDef>;
functions: Record<string, AquaFunction>;
functionCall?: AquaFunction;
errors: string[];
warnings: string[];
generatedSources: GeneratedSource[];
}

Expand Down
135 changes: 76 additions & 59 deletions api/api/.js/src/main/scala/api/AquaAPI.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package api

import api.types.{AquaConfig, AquaFunction, CompilationResult, GeneratedSource, Input}
import aqua.ErrorRendering.showError
import aqua.Rendering.given
import aqua.raw.value.ValueRaw
import aqua.api.{APICompilation, AquaAPIConfig}
import aqua.api.{APICompilation, APIResult, AquaAPIConfig}
import aqua.api.TargetType.*
import aqua.backend.air.AirBackend
import aqua.backend.{AirFunction, Backend, Generated}
Expand All @@ -13,18 +13,24 @@ import aqua.logging.{LogFormatter, LogLevels}
import aqua.constants.Constants
import aqua.io.*
import aqua.raw.ops.Call
import aqua.run.{CallInfo, CallPreparer, CliFunc, FuncCompiler, RunPreparer}
import aqua.run.{CliFunc, FuncCompiler}
import aqua.parser.lexer.{LiteralToken, Token}
import aqua.parser.lift.FileSpan.F
import aqua.parser.lift.{FileSpan, Span}
import aqua.parser.{ArrowReturnError, BlockIndentError, LexerError, ParserError}
import aqua.semantics.{CompilerState, HeaderError, RulesViolated, WrongAST}
import aqua.{AquaIO, SpanParser}
import aqua.model.transform.{Transform, TransformConfig}
import aqua.backend.api.APIBackend
import aqua.backend.js.JavaScriptBackend
import aqua.backend.ts.TypeScriptBackend
import aqua.definitions.FunctionDef
import aqua.js.{FunctionDefJs, ServiceDefJs, VarJson}
import aqua.model.AquaContext
import aqua.raw.ops.CallArrowRawTag
import aqua.raw.value.{LiteralRaw, VarRaw}
import aqua.res.AquaRes

import cats.Applicative
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
import cats.data.Validated.{invalidNec, validNec, Invalid, Valid}
import cats.syntax.applicative.*
Expand All @@ -35,6 +41,7 @@ import cats.effect.IO
import cats.effect.unsafe.implicits.global
import cats.syntax.show.*
import cats.syntax.traverse.*
import cats.syntax.either.*
import fs2.io.file.{Files, Path}
import scribe.Logging

Expand All @@ -44,12 +51,6 @@ import scala.scalajs.js.{|, undefined, Promise, UndefOr}
import scala.scalajs.js
import scala.scalajs.js.JSConverters.*
import scala.scalajs.js.annotation.*
import aqua.js.{FunctionDefJs, ServiceDefJs, VarJson}
import aqua.model.AquaContext
import aqua.raw.ops.CallArrowRawTag
import aqua.raw.value.{LiteralRaw, VarRaw}
import aqua.res.AquaRes
import cats.Applicative

@JSExportTopLevel("Aqua")
object AquaAPI extends App with Logging {
Expand All @@ -68,11 +69,9 @@ object AquaAPI extends App with Logging {
aquaConfigJS: js.UndefOr[AquaConfig]
): Promise[CompilationResult] = {
aquaConfigJS.toOption
.map(cjs => AquaConfig.fromJS(cjs))
.getOrElse(
validNec(AquaAPIConfig())
)
.map { config =>
.map(AquaConfig.fromJS)
.getOrElse(validNec(AquaAPIConfig()))
.traverse { config =>
val importsList = imports.toList

input match {
Expand All @@ -82,10 +81,10 @@ object AquaAPI extends App with Logging {
compileCall(c, importsList, config)

}
} match {
case Valid(v) => v.unsafeToFuture().toJSPromise
case Invalid(errs) => js.Promise.resolve(CompilationResult.errs(errs.toChain.toList))
}
}
.map(_.leftMap(errs => CompilationResult.errs(errs.toChain.toList)).merge)
.unsafeToFuture()
.toJSPromise
}

// Compile all non-call inputs
Expand All @@ -100,15 +99,17 @@ object AquaAPI extends App with Logging {
case JavaScriptType => JavaScriptBackend()
}

extension (res: IO[ValidatedNec[String, Chain[AquaCompiled[FileModuleId]]]])
def toResult: IO[CompilationResult] = res.map { compiledV =>
compiledV.map { compiled =>
config.targetType match {
case AirType => generatedToAirResult(compiled)
case TypeScriptType => compiledToTsSourceResult(compiled)
case JavaScriptType => compiledToJsSourceResult(compiled)
}
}.leftMap(errorsToResult).merge
extension (res: APIResult[Chain[AquaCompiled[FileModuleId]]])
def toResult: CompilationResult = {
val (warnings, result) = res.value.run

result.map { compiled =>
(config.targetType match {
case AirType => generatedToAirResult
case TypeScriptType => compiledToTsSourceResult
case JavaScriptType => compiledToJsSourceResult
}).apply(compiled, warnings)
}.leftMap(errorsToResult(_, warnings)).merge
}

input match {
Expand All @@ -120,7 +121,7 @@ object AquaAPI extends App with Logging {
config,
backend
)
.toResult
.map(_.toResult)
case p: types.Path =>
APICompilation
.compilePath(
Expand All @@ -129,23 +130,33 @@ object AquaAPI extends App with Logging {
config,
backend
)
.toResult
.map(_.toResult)
}

}

private def compileCall(call: types.Call, imports: List[String], config: AquaAPIConfig) = {
// Compile a function call
private def compileCall(
call: types.Call,
imports: List[String],
config: AquaAPIConfig
): IO[CompilationResult] = {
val path = call.input match {
case i: types.Input => i.input
case p: types.Path => p.path
}

extension (res: IO[ValidatedNec[String, (FunctionDef, String)]])
def callToResult: IO[CompilationResult] = res.map(
_.map { case (definitions, air) =>
CompilationResult.result(call = Some(AquaFunction(FunctionDefJs(definitions), air)))
}.leftMap(errorsToResult).merge
)
extension (res: APIResult[(FunctionDef, String)])
def callToResult: CompilationResult = {
val (warnings, result) = res.value.run

result.map { case (definitions, air) =>
CompilationResult.result(
call = Some(AquaFunction(FunctionDefJs(definitions), air)),
warnings = warnings.toList
)
}.leftMap(errorsToResult(_, warnings)).merge
}

APICompilation
.compileCall(
Expand All @@ -155,34 +166,36 @@ object AquaAPI extends App with Logging {
config,
vr => VarJson.checkDataGetServices(vr, Some(call.arguments)).map(_._1)
)
.callToResult
}

private def errorsToResult(errors: NonEmptyChain[String]): CompilationResult = {
CompilationResult.errs(errors.toChain.toList)
.map(_.callToResult)
}

extension (res: List[GeneratedSource])

def toSourcesResult: CompilationResult =
CompilationResult.result(sources = res.toJSArray)
private def errorsToResult(
InversionSpaces marked this conversation as resolved.
Show resolved Hide resolved
errors: NonEmptyChain[String],
warnings: Chain[String]
): CompilationResult = CompilationResult.errs(
InversionSpaces marked this conversation as resolved.
Show resolved Hide resolved
errors.toChain.toList,
warnings.toList
)

private def compiledToTsSourceResult(
compiled: Chain[AquaCompiled[FileModuleId]]
): CompilationResult =
compiled.toList
compiled: Chain[AquaCompiled[FileModuleId]],
warnings: Chain[String]
): CompilationResult = CompilationResult.result(
sources = compiled.toList
.flatMap(c =>
c.compiled
.find(_.suffix == TypeScriptBackend.ext)
.map(_.content)
.map(GeneratedSource.tsSource(c.sourceId.toString, _))
)
.toSourcesResult
),
warnings = warnings.toList
)

private def compiledToJsSourceResult(
compiled: Chain[AquaCompiled[FileModuleId]]
): CompilationResult =
compiled.toList.flatMap { c =>
compiled: Chain[AquaCompiled[FileModuleId]],
warnings: Chain[String]
): CompilationResult = CompilationResult.result(
sources = compiled.toList.flatMap { c =>
for {
dtsContent <- c.compiled
.find(_.suffix == JavaScriptBackend.dtsExt)
Expand All @@ -191,20 +204,24 @@ object AquaAPI extends App with Logging {
.find(_.suffix == JavaScriptBackend.ext)
.map(_.content)
} yield GeneratedSource.jsSource(c.sourceId.toString, jsContent, dtsContent)
}.toSourcesResult
},
warnings = warnings.toList
)

private def generatedToAirResult(
compiled: Chain[AquaCompiled[FileModuleId]]
compiled: Chain[AquaCompiled[FileModuleId]],
warnings: Chain[String]
): CompilationResult = {
val generated = compiled.toList.flatMap(_.compiled)
val serviceDefs = generated.flatMap(_.services).map(s => s.name -> ServiceDefJs(s))
val functions = generated.flatMap(
_.air.map(as => (as.name, AquaFunction(FunctionDefJs(as.funcDef), as.air)))
_.air.map(as => as.name -> AquaFunction(FunctionDefJs(as.funcDef), as.air))
)

CompilationResult.result(
js.Dictionary.apply(serviceDefs: _*),
js.Dictionary.apply(functions: _*)
services = serviceDefs.toMap,
functions = functions.toMap,
warnings = warnings.toList
)

}
Expand Down
43 changes: 33 additions & 10 deletions api/api/.js/src/main/scala/api/types/OutputTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package api.types

import aqua.js.{FunctionDefJs, ServiceDefJs}
import aqua.model.transform.TransformConfig
import cats.data.Validated.{Invalid, Valid, invalidNec, validNec}

import cats.data.Validated.{invalidNec, validNec, Invalid, Valid}
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}

import scala.scalajs.js
Expand Down Expand Up @@ -31,8 +32,12 @@ case class GeneratedSource(
)

object GeneratedSource {
def tsSource(name: String, tsSource: String) = new GeneratedSource(name, tsSource, null, null)
def jsSource(name: String, jsSource: String, tsTypes: String) = new GeneratedSource(name, null, jsSource, tsTypes)

def tsSource(name: String, tsSource: String) =
new GeneratedSource(name, tsSource, null, null)

def jsSource(name: String, jsSource: String, tsTypes: String) =
new GeneratedSource(name, null, jsSource, tsTypes)
}

@JSExportTopLevel("CompilationResult")
Expand All @@ -46,21 +51,39 @@ class CompilationResult(
@JSExport
val generatedSources: js.Array[GeneratedSource],
@JSExport
val errors: js.Array[String]
val errors: js.Array[String],
@JSExport
val warnings: js.Array[String]
)

object CompilationResult {

def result(
services: js.Dictionary[ServiceDefJs] = js.Dictionary(),
functions: js.Dictionary[AquaFunction] = js.Dictionary(),
services: Map[String, ServiceDefJs] = Map.empty,
functions: Map[String, AquaFunction] = Map.empty,
call: Option[AquaFunction] = None,
sources: js.Array[GeneratedSource] = js.Array()
sources: List[GeneratedSource] = List.empty,
warnings: List[String] = List.empty
): CompilationResult =
new CompilationResult(services, functions, call.orNull, sources, js.Array())
new CompilationResult(
services.toJSDictionary,
functions.toJSDictionary,
call.orNull,
sources.toJSArray,
js.Array(),
warnings.toJSArray
)

def errs(
errors: List[String]
errors: List[String] = List.empty,
warnings: List[String] = List.empty
): CompilationResult =
CompilationResult(js.Dictionary(), js.Dictionary(), null, null, errors.toJSArray)
new CompilationResult(
js.Dictionary.empty,
js.Dictionary.empty,
null,
null,
errors.toJSArray,
warnings.toJSArray
)
}
Loading