mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 14:40:17 +00:00
Language server (#512)
This commit is contained in:
parent
2ff870dd9a
commit
16a802f5a5
25
.github/workflows/release.yml
vendored
25
.github/workflows/release.yml
vendored
@ -27,6 +27,11 @@ jobs:
|
||||
env:
|
||||
BUILD_NUMBER: ${{ github.run_number }}
|
||||
|
||||
- name: JS language server API build
|
||||
run: sbt language-server-api/fullLinkJS
|
||||
env:
|
||||
BUILD_NUMBER: ${{ github.run_number }}
|
||||
|
||||
- name: Get project version
|
||||
# In CI sbt appends a new line after its output, so we need `tail -n3 | head -n2` to get last two non-empty lines
|
||||
run: |
|
||||
@ -56,6 +61,13 @@ jobs:
|
||||
stat "$JS"
|
||||
echo "JS=$JS" >> $GITHUB_ENV
|
||||
|
||||
- name: Check API .js exists
|
||||
run: |
|
||||
JSAPI="language-server-api/target/scala-3.1.0/language-server-api-opt/aqua-${{ env.VERSION }}.js"
|
||||
mv language-server-api/target/scala-3.1.0/language-server-api-opt/main.js "$JSAPI"
|
||||
stat "$JSAPI"
|
||||
echo "JSAPI=$JSAPI" >> $GITHUB_ENV
|
||||
|
||||
### Publish to NPM registry
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
@ -63,6 +75,7 @@ jobs:
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- run: cp ${{ env.JS }} ./npm/aqua.js
|
||||
- run: cp ${{ env.JSAPI }} ./language-server-npm/aqua-lsp-api.js
|
||||
|
||||
- run: npm version ${{ env.VERSION }}
|
||||
working-directory: ./npm
|
||||
@ -111,3 +124,15 @@ jobs:
|
||||
${{ env.JS }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- run: npm version ${{ env.VERSION }}
|
||||
working-directory: ./language-server-npm
|
||||
|
||||
- name: Publish aqua LSP API to NPM
|
||||
run: |
|
||||
npm i
|
||||
npm publish --access public
|
||||
working-directory: ./language-server-npm
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
|
36
build.sbt
36
build.sbt
@ -46,13 +46,11 @@ lazy val cli = crossProject(JSPlatform, JVMPlatform)
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.typelevel" %%% "cats-effect" % catsEffectV,
|
||||
"com.monovore" %%% "decline" % declineV,
|
||||
"com.monovore" %%% "decline-effect" % declineV,
|
||||
"co.fs2" %%% "fs2-io" % fs2V
|
||||
"com.monovore" %%% "decline" % declineV,
|
||||
"com.monovore" %%% "decline-effect" % declineV
|
||||
)
|
||||
)
|
||||
.dependsOn(compiler, `backend-air`, `backend-ts`)
|
||||
.dependsOn(compiler, `backend-air`, `backend-ts`, io)
|
||||
|
||||
lazy val cliJS = cli.js
|
||||
.settings(
|
||||
@ -69,6 +67,34 @@ lazy val cliJVM = cli.jvm
|
||||
)
|
||||
)
|
||||
|
||||
lazy val io = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.typelevel" %%% "cats-effect" % catsEffectV,
|
||||
"co.fs2" %%% "fs2-io" % fs2V
|
||||
)
|
||||
)
|
||||
.dependsOn(compiler, parser)
|
||||
|
||||
lazy val `language-server-api` = project
|
||||
.in(file("language-server-api"))
|
||||
.enablePlugins(ScalaJSPlugin)
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
|
||||
scalaJSUseMainModuleInitializer := true
|
||||
)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.typelevel" %%% "cats-effect" % catsEffectV,
|
||||
"co.fs2" %%% "fs2-io" % fs2V
|
||||
)
|
||||
)
|
||||
.dependsOn(compiler.js, io.js)
|
||||
|
||||
lazy val types = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package aqua.builder
|
||||
|
||||
import aqua.backend.*
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.js.{CallJsFunction, FluencePeer, ServiceHandler}
|
||||
import aqua.model.{LiteralModel, VarModel}
|
||||
import aqua.raw.ops.{Call, CallArrowRawTag}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package aqua.builder
|
||||
|
||||
import aqua.backend.*
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.js.{CallJsFunction, CallServiceHandler, FluencePeer, ServiceHandler}
|
||||
import cats.data.NonEmptyList
|
||||
import scribe.Logging
|
||||
|
@ -13,13 +13,11 @@ import aqua.{
|
||||
RunInfo,
|
||||
SubCommandBuilder
|
||||
}
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.js.{Fluence, PeerConfig}
|
||||
import aqua.keypair.KeyPairShow.show
|
||||
import cats.data.{NonEmptyChain, NonEmptyList, Validated, ValidatedNec, ValidatedNel}
|
||||
import Validated.{invalid, invalidNec, valid, validNec, validNel}
|
||||
import aqua.builder.IPFSUploader
|
||||
import aqua.files.AquaFilesIO
|
||||
import aqua.ipfs.js.IpfsApi
|
||||
import aqua.model.LiteralModel
|
||||
import aqua.raw.value.LiteralRaw
|
||||
|
@ -2,7 +2,6 @@ package aqua.remote
|
||||
|
||||
import aqua.builder.IPFSUploader
|
||||
import DistOpts.*
|
||||
import aqua.files.AquaFilesIO
|
||||
import aqua.ipfs.IpfsOpts.{pathOpt, UploadFuncName}
|
||||
import aqua.js.FluenceEnvironment
|
||||
import aqua.model.{LiteralModel, ValueModel}
|
||||
|
@ -2,7 +2,6 @@ package aqua.run
|
||||
|
||||
import aqua.ArgOpts.checkDataGetServices
|
||||
import aqua.builder.{ArgumentGetter, Service}
|
||||
import aqua.files.AquaFilesIO
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
|
@ -6,8 +6,6 @@ import aqua.backend.Generated
|
||||
import aqua.backend.air.{AirBackend, AirGen, FuncAirGen}
|
||||
import aqua.builder.ArgumentGetter
|
||||
import aqua.compiler.AquaCompiler
|
||||
import aqua.files.{AquaFileSources, AquaFilesIO, FileModuleId}
|
||||
import aqua.io.{AquaFileError, OutputPrinter}
|
||||
import aqua.ipfs.js.IpfsApi
|
||||
import aqua.js.{Config, Fluence, PeerConfig}
|
||||
import aqua.keypair.KeyPairShow.show
|
||||
|
@ -10,6 +10,7 @@ import org.scalatest.matchers.should.Matchers
|
||||
import fs2.io.file.{Files, Path}
|
||||
|
||||
class WriteFileSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
"cli" should "compile aqua code in js" in {
|
||||
val src = Path("./cli/.jvm/src/test/aqua")
|
||||
val targetTs = Files[IO].createTempDirectory.unsafeRunSync()
|
||||
|
@ -4,7 +4,7 @@ import aqua.compiler.*
|
||||
import aqua.files.FileModuleId
|
||||
import aqua.io.AquaFileError
|
||||
import aqua.parser.lift.{FileSpan, Span}
|
||||
import aqua.parser.{ArrowReturnError, BlockIndentError, LexerError}
|
||||
import aqua.parser.{ArrowReturnError, BlockIndentError, LexerError, ParserError}
|
||||
import aqua.semantics.{HeaderError, RulesViolated, WrongAST}
|
||||
import cats.parse.LocationMap
|
||||
import cats.parse.Parser.Expectation
|
||||
@ -13,32 +13,6 @@ import cats.{Eval, Show}
|
||||
|
||||
object ErrorRendering {
|
||||
|
||||
def betterSymbol(symbol: Char): String = {
|
||||
symbol match {
|
||||
case ' ' => "whitespace"
|
||||
case '\t' => "tabulation"
|
||||
case c => c.toString
|
||||
}
|
||||
}
|
||||
|
||||
def expectationToString(expectation: Expectation, acc: List[String] = Nil): List[String] = {
|
||||
// TODO: match all expectations
|
||||
expectation match {
|
||||
// get the deepest context
|
||||
case WithContext(str, exp: WithContext) => expectationToString(exp, List(str))
|
||||
case WithContext(str, exp) => s"$str (${expectationToString(exp)})" +: acc
|
||||
case FailWith(_, message) => message +: acc
|
||||
case InRange(offset, lower, upper) =>
|
||||
if (lower == upper)
|
||||
s"Expected symbol '${betterSymbol(lower)}'" +: acc
|
||||
else
|
||||
s"Expected symbols from '${betterSymbol(lower)}' to '${betterSymbol(upper)}'" +: acc
|
||||
case OneOfStr(offset, strs) =>
|
||||
s"Expected one of these strings: ${strs.map(s => s"'$s'").mkString(", ")}" +: acc
|
||||
case e => ("Expected: " + e.toString) +: acc
|
||||
}
|
||||
}
|
||||
|
||||
def showForConsole(errorType: String, span: FileSpan, messages: List[String]): String =
|
||||
span
|
||||
.focus(3)
|
||||
@ -70,7 +44,7 @@ object ErrorRendering {
|
||||
val msg = FileSpan(span.name, span.locationMap, localSpan)
|
||||
.focus(0)
|
||||
.map { spanFocus =>
|
||||
val errorMessages = exps.flatMap(exp => expectationToString(exp))
|
||||
val errorMessages = exps.flatMap(exp => ParserError.expectationToString(exp))
|
||||
spanFocus.toConsoleStr(
|
||||
"Syntax error",
|
||||
s"${errorMessages.head}" :: errorMessages.tail.map(t => "OR " + t),
|
||||
@ -113,7 +87,7 @@ object ErrorRendering {
|
||||
.map(_.toConsoleStr("Header error", message :: Nil, Console.CYAN))
|
||||
.getOrElse("(Dup error, but offset is beyond the script)")
|
||||
case WrongAST(ast) =>
|
||||
s"Semantic error: wrong AST"
|
||||
"Semantic error: wrong AST"
|
||||
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,6 @@ object SpanParser extends scribe.Logging {
|
||||
)
|
||||
}
|
||||
}
|
||||
import Span.spanLiftParser
|
||||
val parser = Parser.natParser(Parser.spanParser, nat)(source)
|
||||
logger.trace("parser created")
|
||||
parser
|
@ -118,7 +118,12 @@ class AquaFileSources[F[_]: AquaIO: Monad: Files: Functor](
|
||||
}
|
||||
|
||||
// Write content to a file and return a success message
|
||||
private def writeWithResult(target: Path, content: String, funcsCount: Int, servicesCount: Int) = {
|
||||
private def writeWithResult(
|
||||
target: Path,
|
||||
content: String,
|
||||
funcsCount: Int,
|
||||
servicesCount: Int
|
||||
) = {
|
||||
filesIO
|
||||
.writeFile(
|
||||
target,
|
@ -78,12 +78,18 @@ class AquaFilesIO[F[_]: Files: Concurrent] extends AquaIO[F] {
|
||||
)
|
||||
|
||||
// Get all files for every path if the path in the list is a directory or this path otherwise
|
||||
private def gatherFiles(files: List[Path], listFunction: (f: Path) => F[ValidatedNec[AquaFileError, Chain[Path]]]): List[F[ValidatedNec[AquaFileError, Chain[Path]]]] = {
|
||||
private def gatherFiles(
|
||||
files: List[Path],
|
||||
listFunction: (f: Path) => F[ValidatedNec[AquaFileError, Chain[Path]]]
|
||||
): List[F[ValidatedNec[AquaFileError, Chain[Path]]]] = {
|
||||
files.map(f => gatherFile(f, listFunction))
|
||||
}
|
||||
|
||||
// Get all files if the path is a directory or this path otherwise
|
||||
private def gatherFile(f: Path, listFunction: (f: Path) => F[ValidatedNec[AquaFileError, Chain[Path]]]): F[ValidatedNec[AquaFileError, Chain[Path]]] = {
|
||||
private def gatherFile(
|
||||
f: Path,
|
||||
listFunction: (f: Path) => F[ValidatedNec[AquaFileError, Chain[Path]]]
|
||||
): F[ValidatedNec[AquaFileError, Chain[Path]]] = {
|
||||
Files[F].isDirectory(f).flatMap { isDir =>
|
||||
if (isDir)
|
||||
listFunction(f)
|
||||
@ -107,8 +113,15 @@ class AquaFilesIO[F[_]: Files: Concurrent] extends AquaIO[F] {
|
||||
} else {
|
||||
Files[F].isDirectory(folder).flatMap { isDir =>
|
||||
if (isDir) {
|
||||
Files[F].list(folder).evalFilter(p => if (p.extName == ".aqua") true.pure[F] else Files[F].isDirectory(p))
|
||||
.compile.toList.map(Right(_))
|
||||
Files[F]
|
||||
.list(folder)
|
||||
.evalFilter(p =>
|
||||
if (p.extName == ".aqua") true.pure[F]
|
||||
else Files[F].isDirectory(p)
|
||||
)
|
||||
.compile
|
||||
.toList
|
||||
.map(Right(_))
|
||||
} else {
|
||||
Right(folder :: Nil).pure[F]
|
||||
}
|
131
language-server-api/src/main/scala/aqua/lsp/AquaLSP.scala
Normal file
131
language-server-api/src/main/scala/aqua/lsp/AquaLSP.scala
Normal file
@ -0,0 +1,131 @@
|
||||
package aqua.lsp
|
||||
|
||||
import aqua.compiler.*
|
||||
import aqua.files.{AquaFileSources, AquaFilesIO, FileModuleId}
|
||||
import aqua.io.*
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.parser.lift.{FileSpan, Span}
|
||||
import aqua.parser.{ArrowReturnError, BlockIndentError, LexerError, ParserError}
|
||||
import aqua.semantics.{HeaderError, RulesViolated, WrongAST}
|
||||
import aqua.{AquaIO, SpanParser}
|
||||
import cats.data.NonEmptyChain
|
||||
import cats.data.Validated.{Invalid, Valid}
|
||||
import cats.effect.IO
|
||||
import cats.effect.unsafe.implicits.global
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
import scala.scalajs.js.JSConverters.*
|
||||
import scala.scalajs.js.annotation.*
|
||||
import scala.scalajs.js.{undefined, UndefOr}
|
||||
|
||||
@JSExportAll
|
||||
case class ErrorInfo(start: Int, end: Int, message: String, location: UndefOr[String])
|
||||
|
||||
object ErrorInfo {
|
||||
|
||||
def apply(fileSpan: FileSpan, message: String): ErrorInfo = {
|
||||
val start = fileSpan.span.startIndex
|
||||
val end = fileSpan.span.endIndex
|
||||
ErrorInfo(start, end, message, fileSpan.name)
|
||||
}
|
||||
|
||||
def applyOp(start: Int, end: Int, message: String, location: Option[String]): ErrorInfo = {
|
||||
ErrorInfo(start, end, message, location.getOrElse(undefined))
|
||||
}
|
||||
}
|
||||
|
||||
@JSExportTopLevel("AquaLSP")
|
||||
object AquaLSP extends App with Logging {
|
||||
|
||||
def errorToInfo(error: AquaError[FileModuleId, AquaFileError, FileSpan.F]): List[ErrorInfo] = {
|
||||
error match {
|
||||
case ParserErr(err) =>
|
||||
err match {
|
||||
case BlockIndentError(indent, message) =>
|
||||
ErrorInfo(indent._1, message) :: Nil
|
||||
case ArrowReturnError(point, message) =>
|
||||
ErrorInfo(point._1, message) :: Nil
|
||||
case LexerError((span, e)) =>
|
||||
e.expected.toList
|
||||
.groupBy(_.offset)
|
||||
.map { case (offset, exps) =>
|
||||
val localSpan = Span(offset, offset + 1)
|
||||
val fSpan = FileSpan(span.name, span.locationMap, localSpan)
|
||||
val errorMessages = exps.flatMap(exp => ParserError.expectationToString(exp))
|
||||
val msg = s"${errorMessages.head}" :: errorMessages.tail.map(t => "OR " + t)
|
||||
(offset, ErrorInfo(fSpan, msg.mkString("\n")))
|
||||
}
|
||||
.toList
|
||||
.sortBy(_._1)
|
||||
.map(_._2)
|
||||
.reverse
|
||||
}
|
||||
case SourcesErr(err) =>
|
||||
ErrorInfo.applyOp(0, 0, err.showForConsole, None) :: Nil
|
||||
case ResolveImportsErr(_, token, err) =>
|
||||
ErrorInfo(token.unit._1, err.showForConsole) :: Nil
|
||||
case ImportErr(token) =>
|
||||
ErrorInfo(token.unit._1, "Cannot resolve import") :: Nil
|
||||
case CycleError(modules) =>
|
||||
ErrorInfo.applyOp(
|
||||
0,
|
||||
0,
|
||||
s"Cycle loops detected in imports: ${modules.map(_.file.fileName)}",
|
||||
None
|
||||
) :: Nil
|
||||
case CompileError(err) =>
|
||||
err match {
|
||||
case RulesViolated(token, messages) =>
|
||||
ErrorInfo(token.unit._1, messages.mkString("\n")) :: Nil
|
||||
case HeaderError(token, message) =>
|
||||
ErrorInfo(token.unit._1, message) :: Nil
|
||||
case WrongAST(ast) =>
|
||||
ErrorInfo.applyOp(0, 0, "Semantic error: wrong AST", None) :: Nil
|
||||
|
||||
}
|
||||
case OutputError(_, err) =>
|
||||
ErrorInfo.applyOp(0, 0, err.showForConsole, None) :: Nil
|
||||
}
|
||||
}
|
||||
|
||||
@JSExport
|
||||
def compile(
|
||||
pathStr: String,
|
||||
imports: scalajs.js.Array[String]
|
||||
): scalajs.js.Promise[scalajs.js.Array[ErrorInfo]] = {
|
||||
|
||||
logger.debug(s"Compiling '$pathStr' with imports: $imports")
|
||||
|
||||
implicit val aio: AquaIO[IO] = new AquaFilesIO[IO]
|
||||
|
||||
val sources = new AquaFileSources[IO](Path(pathStr), imports.toList.map(Path.apply))
|
||||
val config = TransformConfig()
|
||||
|
||||
val proc = for {
|
||||
res <- AquaCompiler
|
||||
.compileToContext[IO, AquaFileError, FileModuleId, FileSpan.F](
|
||||
sources,
|
||||
SpanParser.parser,
|
||||
config
|
||||
)
|
||||
} yield {
|
||||
logger.debug("Compilation done.")
|
||||
val result = res match {
|
||||
case Valid(_) =>
|
||||
logger.debug("No errors on compilation.")
|
||||
List.empty.toJSArray
|
||||
case Invalid(e: NonEmptyChain[AquaError[FileModuleId, AquaFileError, FileSpan.F]]) =>
|
||||
val errors = e.toNonEmptyList.toList.flatMap(errorToInfo)
|
||||
logger.debug("Errors: " + errors.mkString("\n"))
|
||||
errors.toJSArray
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
proc.unsafeToFuture().toJSPromise
|
||||
|
||||
}
|
||||
}
|
12
language-server-npm/aqua-lsp-api.d.ts
vendored
Normal file
12
language-server-npm/aqua-lsp-api.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
export interface ErrorInfo {
|
||||
start: number,
|
||||
end: number,
|
||||
message: string,
|
||||
location: string | null
|
||||
}
|
||||
|
||||
export class Compiler {
|
||||
compile(path: string, imports: string[]): Promise<ErrorInfo[]>;
|
||||
}
|
||||
|
||||
export var AquaLSP: Compiler;
|
13
language-server-npm/package-lock.json
generated
Normal file
13
language-server-npm/package-lock.json
generated
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@fluencelabs/aqua-language-server-api",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@fluencelabs/aqua-language-server-api",
|
||||
"version": "0.0.0",
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
}
|
||||
}
|
27
language-server-npm/package.json
Normal file
27
language-server-npm/package.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "@fluencelabs/aqua-language-server-api",
|
||||
"version": "0.0.3",
|
||||
"description": "Aqua Language Server API",
|
||||
"type": "commonjs",
|
||||
"files": [
|
||||
"aqua-lsp-api.js",
|
||||
"aqua-lsp-api.d.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"move:scalajs": "cp ../language-server-api/target/scala-3.1.0/language-server-opt/main.js ./aqua-lsp-api.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/fluencelabs/aqua.git"
|
||||
},
|
||||
"keywords": [
|
||||
"aqua",
|
||||
"fluence"
|
||||
],
|
||||
"author": "Fluence Labs",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/fluencelabs/aqua/issues"
|
||||
},
|
||||
"homepage": "https://github.com/fluencelabs/aqua#readme"
|
||||
}
|
@ -1,8 +1,35 @@
|
||||
{
|
||||
"target": "12D3KooWMhVpgfQxBLkQkJed8VFNvgN4iE6MD7xCybb1ZYWW2Gtz",
|
||||
"validators": [
|
||||
"12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er",
|
||||
"12D3KooWBUJifCTgaxAUrcM9JysqCcS4CS8tiYH5hExbdWCAoNwb",
|
||||
"12D3KooWJbJFaZ3k5sNd8DjQgg3aERoKtBAnirEvPV8yp76kEXHB",
|
||||
"12D3KooWCKCeqLPSgMnDjyFsJuWqREDtKNHx1JEBiwaMXhCLNTRb",
|
||||
"12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH",
|
||||
"12D3KooWBSdm6TkqnEFrgBuSkpVE3dR1kr6952DsWQRNwJZjFZBv",
|
||||
"12D3KooWGzNvhSDsgFoHwpWHAyPf1kcTYCGeRBPfznL8J6qdyu2H",
|
||||
"12D3KooWF7gjXhQ4LaKj6j7ntxsPpGk34psdQicN2KNfBi9bFKXg",
|
||||
"12D3KooWB9P1xmV3c7ZPpBemovbwCiRRTKd3Kq2jsVPQN4ZukDfy"
|
||||
],
|
||||
"timeout": 5000,
|
||||
"stringField": "some string",
|
||||
"numberField": 123,
|
||||
"structField": {
|
||||
"numField": 42,
|
||||
"arrField": ["str1", "str2"]
|
||||
"arrField": ["str1", "str2", "r43r34", "ferer"],
|
||||
"arr2": [{
|
||||
"a": "fef",
|
||||
"b": [1,2,3,4],
|
||||
"c": "erfer",
|
||||
"d": "frefe"
|
||||
},{
|
||||
"b": [1,2,3,4],
|
||||
"c": "erfer",
|
||||
"d": "frefe"
|
||||
}, {
|
||||
"a": "as",
|
||||
"c": "erfer",
|
||||
"d": "gerrt"
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import "run-builtins.aqua"
|
||||
import "@fluencelabs/aqua-lib/builtin.aqua"
|
||||
-- import "run-builtins.aqua"
|
||||
|
||||
data StructType:
|
||||
numField: u32
|
||||
@ -11,14 +12,33 @@ service OpNumber("op"):
|
||||
identity(n: u32) -> u32
|
||||
|
||||
service OpStruct("op"):
|
||||
identity(st: StructType) -> StructType
|
||||
identity(st: StructType) -> StructType
|
||||
noop()
|
||||
|
||||
func parseBug():
|
||||
stream: *string
|
||||
if stream[0] != "FOO":
|
||||
Op.noop()
|
||||
|
||||
func identityArgsAndReturn(structArg: StructType, stringArg: string, numberArg: u32) -> string, u32, StructType:
|
||||
func identityArgsAndReturn (structArg: StructType, stringArg: string, numberArg: u32) -> string, u32, StructType:
|
||||
on HOST_PEER_ID:
|
||||
sArg <- OpString.identity(stringArg)
|
||||
nArg <- OpNumber.identity(numberArg)
|
||||
nArg = OpNumber.identity (numberArg) + OpNumber.identity (numberArg)
|
||||
stArg <- OpStruct.identity(structArg)
|
||||
-- it could be used only on init_peer_id
|
||||
Console.print("hello")
|
||||
<- sArg, nArg, stArg
|
||||
|
||||
service Ssss("ss"):
|
||||
foo4: u64 -> u16
|
||||
|
||||
func aaa(a: u64) -> u16:
|
||||
res <- Ssss.foo4(a)
|
||||
<- res
|
||||
|
||||
func bar(callback: u32 -> u32):
|
||||
callback(1)
|
||||
|
||||
func baz():
|
||||
bar(aaa)
|
||||
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
package aqua.parser
|
||||
|
||||
import cats.parse.Parser
|
||||
import cats.parse.Parser.Expectation
|
||||
import cats.parse.Parser.Expectation.{FailWith, InRange, OneOfStr, WithContext}
|
||||
import cats.~>
|
||||
|
||||
trait ParserError[F[_]] {
|
||||
@ -22,3 +24,32 @@ case class ArrowReturnError[F[_]](point: F[Unit], message: String) extends Parse
|
||||
def mapK[K[_]](fk: F ~> K): ArrowReturnError[K] =
|
||||
copy(fk(point))
|
||||
}
|
||||
|
||||
object ParserError {
|
||||
|
||||
def betterSymbol(symbol: Char): String = {
|
||||
symbol match {
|
||||
case ' ' => "whitespace"
|
||||
case '\t' => "tabulation"
|
||||
case c => c.toString
|
||||
}
|
||||
}
|
||||
|
||||
def expectationToString(expectation: Expectation, acc: List[String] = Nil): List[String] = {
|
||||
// TODO: match all expectations
|
||||
expectation match {
|
||||
// get the deepest context
|
||||
case WithContext(str, exp: WithContext) => expectationToString(exp, List(str))
|
||||
case WithContext(str, exp) => s"$str (${expectationToString(exp)})" +: acc
|
||||
case FailWith(_, message) => message +: acc
|
||||
case InRange(offset, lower, upper) =>
|
||||
if (lower == upper)
|
||||
s"Expected symbol '${betterSymbol(lower)}'" +: acc
|
||||
else
|
||||
s"Expected symbols from '${betterSymbol(lower)}' to '${betterSymbol(upper)}'" +: acc
|
||||
case OneOfStr(offset, strs) =>
|
||||
s"Expected one of these strings: ${strs.map(s => s"'$s'").mkString(", ")}" +: acc
|
||||
case e => ("Expected: " + e.toString) +: acc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user