Upper cased constants (#260)

This commit is contained in:
Dima 2021-09-07 11:09:48 +03:00 committed by GitHub
parent 621e06dd9c
commit d881f5bdbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 225 additions and 73 deletions

View File

@ -47,18 +47,19 @@ jobs:
sbt test
### Update & build
# - name: Integration test
# run: |
# git clone https://github.com/fluencelabs/aqua-playground.git
# cd aqua-playground
# npm i
# cd ..
# sbt "cli/run -i aqua-playground/aqua/examples -o aqua-playground/src/compiled/examples -m aqua-playground/node_modules -c \"uniqueConst = 1\" -c \"anotherConst = \\\"ab\\\"\""
# cd aqua-playground
# npm run examples
# cd ..
# sbt "cliJS/fastOptJS"
# rm -rf aqua-playground/src/compiled/examples/*
# node cli/.js/target/scala-3.0.1/cli-fastopt.js -i aqua-playground/aqua/examples -o aqua-playground/src/compiled/examples -m aqua-playground/node_modules -c "uniqueConst = 1" -c "anotherConst = \"ab\""
# cd aqua-playground
# npm run examples
- name: Integration test
run: |
git clone https://github.com/fluencelabs/aqua-playground.git
cd aqua-playground
rm -rf src/compiled/examples/*
npm i
cd ..
sbt "cli/run -i aqua-playground/aqua/examples -o aqua-playground/src/compiled/examples -m aqua-playground/node_modules -c \"UNIQUE_CONST = 1\" -c \"ANOTHER_CONST = \\\"ab\\\"\""
cd aqua-playground
npm run examples
cd ..
sbt "cliJS/fastOptJS"
rm -rf aqua-playground/src/compiled/examples/*
node cli/.js/target/scala-3.0.1/cli-fastopt.js -i aqua-playground/aqua/examples -o aqua-playground/src/compiled/examples -m aqua-playground/node_modules -c "UNIQUE_CONST = 1" -c "ANOTHER_CONST = \"ab\""
cd aqua-playground
npm run examples

View File

@ -1,6 +1,6 @@
-Dfile.encoding=UTF8
-Xms1G
-Xmx5G
-Xmx6G
-XX:ReservedCodeCacheSize=500M
-XX:+TieredCompilation
-XX:+UseParallelGC

View File

@ -1,4 +1,6 @@
module Export.Test declares foobar, foo, bar
module Export.Test declares foobar, foo, bar, ExpSrv, FA
const FA = "constant"
func bar() -> string:
<- " I am MyFooBar bar"
@ -12,5 +14,6 @@ func foobar() -> []string:
res <- bar()
<- res
service ExpSrv:
baz()
service ExpSrv("exp-srv"):
baz()
log: string -> ()

View File

@ -1,8 +1,9 @@
-- import.aqua
module Import.Test
import foobar from "export.aqua"
import foobar, FA as C, ExpSrv from "export.aqua"
use foo as f from "export.aqua" as Exp
use foo as f, FA as U from "export.aqua" as Exp
use foo as f, FA from "export.aqua" as Exp1
use "export.aqua"
@ -10,7 +11,13 @@ import "gen/OneMore.aqua"
import OneMore as OM from "gen/OneMore.aqua"
export foo_wrapper as wrap, foobar as barfoo
export consts as main--, foobar as barfoo
func consts():
ExpSrv.log(C)
ExpSrv.log(Exp.U)
ExpSrv.log(Exp1.FA)
ExpSrv.log(Export.Test.FA)
func foo_wrapper() -> string:
z <- Exp.f()
@ -20,4 +27,6 @@ func foo_wrapper() -> string:
OM "ohmygod"
OM.more_call()
OM.consume(q)
ExpSrv.log(C)
ExpSrv.log(Exp.U)
<- z

View File

@ -3,7 +3,7 @@ module Ret
import Service from "service.aqua"
use "error.aqua"
export GetStr, tupleFunc, Service as S
export GetStr, multiReturnFunc, Service as S
service GetStr("multiret-test"):
retStr: string -> string
@ -11,18 +11,21 @@ service GetStr("multiret-test"):
service GetNum("multiret-num"):
retNum: -> u8
const someNum = 5
const someStr = "some-str"
const SOME_NUM = 5
const SOME_STR = "some-str"
func tupleFunc() -> string, u8:
str <- GetStr.retStr(someStr)
str <- GetStr.retStr(SOME_STR)
n <- GetNum.retNum()
Err.Peer.is_connected("Connected?")
<- str, n
func multiReturnFunc(somethingToReturn: []u8, smthOption: ?string) -> []string, u8, string, []u8, ?string, u8:
res: *string
res <- GetStr.retStr(someStr)
res <- GetStr.retStr("random-str")
res <- GetStr.retStr(SOME_STR)
try:
res <- GetStr.retStr("random-str")
catch e:
GetStr.retStr(e.msg)
res, tNum <- tupleFunc()
<- res, 5, someStr, somethingToReturn, smthOption, tNum
<- res, 5, SOME_STR, somethingToReturn, smthOption, tNum

View File

@ -29,10 +29,10 @@ object AirGen extends Logging {
def valueToData(vm: ValueModel): DataView = vm match {
case LiteralModel(value, _) => DataView.StringScalar(value)
case VarModel(name, t, lambda) =>
val n = t match {
val n = (t match {
case _: StreamType => "$" + name
case _ => name
}
}).replace('.', '_')
if (lambda.isEmpty) DataView.Variable(n)
else DataView.VarLens(n, lambdaToString(lambda.toList))
}
@ -43,10 +43,10 @@ object AirGen extends Logging {
case list => list.reduceLeft(SeqGen(_, _))
}
def exportToString(exportTo: Call.Export): String = exportTo match {
def exportToString(exportTo: Call.Export): String = (exportTo match {
case Call.Export(name, _: StreamType) => "$" + name
case Call.Export(name, _) => name
}
}).replace('.', '_')
private def folder(op: ResolvedOp, ops: Chain[AirGen]): Eval[AirGen] =
op match {

View File

@ -17,7 +17,7 @@ val declineV = "2.1.0"
name := "aqua-hll"
val commons = Seq(
baseAquaVersion := "0.2.2",
baseAquaVersion := "0.3.0",
version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"),
scalaVersion := dottyVersion,
libraryDependencies ++= Seq(

View File

@ -16,7 +16,7 @@ object Test extends IOApp.Simple {
start <- IO(System.currentTimeMillis())
_ <- AquaPathCompiler
.compileFilesTo[IO](
Path("./aqua-src"),
Path("./aqua-src/import.aqua"),
List(Path("./aqua")),
Path("./target"),
TypeScriptBackend,

View File

@ -97,12 +97,12 @@ object AppOps {
def constantOpts[F[_]: LiftParser: Comonad]: Opts[List[TransformConfig.Const]] =
Opts
.options[String]("const", "Constant that will be used in an aqua code", "c")
.options[String]("const", "Constant that will be used in an aqua code. Constant name must be upper cased.", "c")
.mapValidated { strs =>
val parsed = strs.map(s => ConstantExpr.onlyLiteral.parseAll(s))
val errors = parsed.collect { case Left(er) =>
er
val errors = parsed.zip(strs).collect { case (Left(er), str) =>
str
}
NonEmptyList
@ -113,7 +113,8 @@ object AppOps {
TransformConfig.Const(v._1.value, LiteralModel(v._2.value, v._2.ts))
})
) { errors =>
Validated.invalid(errors.map(_.toString))
val errorMsgs = errors.map (str => s"Invalid constant definition '$str'.")
Validated.invalid(errorMsgs)
}
}
.withDefault(List.empty)

View File

@ -27,6 +27,9 @@ case class AquaContext(
private def prefixFirst[T](prefix: String, pair: (String, T)): (String, T) =
(prefix + pair._1, pair._2)
def isEmpty: Boolean = this == AquaContext.blank
def nonEmpty: Boolean = !isEmpty
def pick(
name: String,
rename: Option[String],
@ -45,7 +48,19 @@ case class AquaContext(
services = getter(_.services)
)
}
.filter(_.`type`(name).nonEmpty)
.filter(_.nonEmpty)
def pickHeader: AquaContext =
AquaContext.blank.copy(module = module, declares = declares, exports = exports)
def pickDeclared(implicit semi: Semigroup[AquaContext]): AquaContext =
if (module.isEmpty) this
else
declares
.flatMap(pick(_, None))
.foldLeft(pickHeader)(
_ |+| _
)
def allTypes(prefix: String = ""): Map[String, Type] =
abilities
@ -83,6 +98,7 @@ case class AquaContext(
types ++
values.view.mapValues(_.lastType) ++
services.view.mapValues(_.`type`) ++
// TODO do we need to pass abilities as type?
abilities.flatMap { case (n, c) =>
c.`type`(n).map(n -> _)
}

View File

@ -100,6 +100,8 @@ case class VarModel(name: String, `type`: Type, lambda: Chain[LambdaModel] = Cha
case Some(vv) => vv // TODO check that lambda is empty, otherwise error
case None => this // Should not happen
}
override def toString(): String = s"var{$name: " + `type` + s"}.${lambda.toList.mkString(".")}"
}
object VarModel {

View File

@ -24,21 +24,39 @@ case class TransformConfig(
// or relay's variable otherwise
val hostPeerId: TransformConfig.Const =
TransformConfig.Const(
"host_peer_id",
"HOST_PEER_ID",
relayVarName.fold[ValueModel](LiteralModel.initPeerId)(r => VarModel(r, ScalarType.string))
)
val initPeerId: TransformConfig.Const =
TransformConfig.Const(
"INIT_PEER_ID",
LiteralModel.initPeerId
)
val nil: TransformConfig.Const =
TransformConfig.Const(
"nil", // TODO: shouldn't it be NIL?
LiteralModel.nil
)
val lastError: TransformConfig.Const =
TransformConfig.Const(
"LAST_ERROR",
VarModel.lastError
)
val constantsMap =
(hostPeerId :: initPeerId :: nil :: lastError :: constants)
.map(c => c.name -> c.value)
.toMap
implicit val aquaContextMonoid: Monoid[AquaContext] = {
val constantsMap = (hostPeerId :: constants).map(c => c.name -> c.value).toMap
AquaContext
.implicits(
AquaContext.blank
.copy(values =
Map(
VarModel.lastError.name -> VarModel.lastError,
"nil" -> LiteralModel.nil
) ++ constantsMap
)
.copy(values = constantsMap)
)
.aquaContextMonoid
}

View File

@ -22,14 +22,14 @@ object ConstantExpr extends Expr.Leaf {
override def p[F[_]: LiftParser: Comonad]: P[ConstantExpr[F]] =
((((`const` *> ` ` *> Name
.p[F] <* ` `) ~ `?`.?).with1 <* `=` <* ` `) ~ Value.`value`).map {
.upper[F] <* ` `) ~ `?`.?).with1 <* `=` <* ` `) ~ Value.`value`).map {
case ((name, mark), value) =>
ConstantExpr(name, value, mark.nonEmpty)
}
def onlyLiteral[F[_]: LiftParser: Comonad]: P[(Name[F], Literal[F])] =
((((Name
.p[F] <* ` `) ~ `?`.?).with1 <* `=` <* ` `) ~ Value.literal).map { case ((name, _), value) =>
.upper[F] <* ` `) ~ `?`.?).with1 <* `=` <* ` `) ~ Value.literal).map { case ((name, _), value) =>
(name, value)
}
}

View File

@ -27,4 +27,10 @@ object FromExpr {
def importFrom[F[_]: LiftParser: Comonad]: P[NonEmptyList[NameOrAbAs[F]]] =
comma[NameOrAbAs[F]](nameOrAbAs[F]) <* ` ` <* `from`
def show[F[_]](ne: NonEmptyList[NameOrAbAs[F]]): String =
ne.toList.map(_.fold(
non => non._1.value + non._2.map(_.value).fold("")(" as "+_),
non => non._1.value + non._2.map(_.value).fold("")(" as "+_)
)).mkString(", ")
}

View File

@ -8,8 +8,11 @@ import cats.parse.Parser
import cats.~>
case class ImportExpr[F[_]](filename: Literal[F]) extends FilenameExpr[F] {
override def mapK[K[_]: Comonad](fk: F ~> K): ImportExpr[K] =
copy(filename.mapK(fk))
override def toString: String = s"import ${filename.value}"
}
object ImportExpr extends HeaderExpr.Leaf {

View File

@ -12,8 +12,11 @@ case class ImportFromExpr[F[_]](
imports: NonEmptyList[FromExpr.NameOrAbAs[F]],
filename: Literal[F]
) extends FilenameExpr[F] with FromExpr[F] {
override def mapK[K[_]: Comonad](fk: F ~> K): ImportFromExpr[K] =
copy(FromExpr.mapK(imports)(fk), filename.mapK(fk))
override def toString: String = s"import ${FromExpr.show(imports)} from ${filename.value}"
}
object ImportFromExpr extends HeaderExpr.Leaf {

View File

@ -11,8 +11,12 @@ case class UseExpr[F[_]](
filename: Literal[F],
asModule: Option[Ability[F]]
) extends FilenameExpr[F] {
override def mapK[K[_]: Comonad](fk: F ~> K): UseExpr[K] =
copy(filename.mapK(fk), asModule.map(_.mapK(fk)))
override def toString(): String =
s"use ${filename.value}${asModule.map(_.value).fold("")(" as " + _)}"
}
object UseExpr extends HeaderExpr.Leaf {

View File

@ -16,6 +16,9 @@ case class UseFromExpr[F[_]](
override def mapK[K[_]: Comonad](fk: F ~> K): UseFromExpr[K] =
copy(FromExpr.mapK(imports)(fk), filename.mapK(fk), asModule.mapK(fk))
override def toString(): String =
s"use ${FromExpr.show(imports)} from ${filename.value} as ${asModule.value}"
}
object UseFromExpr extends HeaderExpr.Leaf {

View File

@ -24,8 +24,11 @@ object Name {
def p[F[_]: LiftParser: Comonad]: P[Name[F]] =
`name`.lift.map(Name(_))
def upper[F[_]: LiftParser: Comonad]: P[Name[F]] =
NAME.lift.map(Name(_))
def dotted[F[_]: LiftParser: Comonad]: P[Name[F]] =
((`Class`.repSep(`.`).map(_.toList.mkString(".")) ~ `.`).?.with1 ~ `name`).string.lift
((`Class` ~ `.`).backtrack.rep0.?.with1 ~ P.oneOf(`name` :: NAME :: Nil)).string.lift
.map(Name(_))
def nameAs[F[_]: LiftParser: Comonad]: P[As[F]] =

View File

@ -19,8 +19,10 @@ object Token {
private val AZ = ('A' to 'Z').toSet
private val f09 = ('0' to '9').toSet
private val anum = az ++ AZ ++ f09
private val upperAnum = AZ ++ f09
private val f_ = Set('_')
private val anum_ = anum ++ f_
private val upperAnum_ = upperAnum ++ f_
private val nl = Set('\n', '\r')
val ` *` : P0[String] = P.charsWhile0(fSpaces)
@ -59,9 +61,10 @@ object Token {
val ` : ` : P[Unit] = P.char(':').surroundedBy(` `.?)
val `anum_*` : P[Unit] = P.charsWhile(anum_).void
val NAME: P[String] = P.charsWhile(upperAnum_).string
val `name`: P[String] = (P.charIn(az) ~ P.charsWhile(anum_).?).string
val `Class`: P[String] = (P.charIn(AZ) ~ P.charsWhile(anum_).?).map { case (c, s)
val `Class`: P[String] = (P.charIn(AZ) ~ P.charsWhile(anum_).backtrack.?).map { case (c, s)
c.toString ++ s.getOrElse("")
}
val `\n` : P[Unit] = P.string("\n\r") | P.char('\n') | P.string("\r\n")

View File

@ -18,16 +18,16 @@ class AssignmentExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
AssignmentExpr[Id]("a", toVar("b"))
)
parseConstant("const a = b") should be(
ConstantExpr[Id]("a", toVar("b"), skipIfAlreadyDefined = false)
parseConstant("const A = B") should be(
ConstantExpr[Id]("A", toVar("B"), skipIfAlreadyDefined = false)
)
parseConstant("const a = 1") should be(
ConstantExpr[Id]("a", toNumber(1), skipIfAlreadyDefined = false)
parseConstant("const A = 1") should be(
ConstantExpr[Id]("A", toNumber(1), skipIfAlreadyDefined = false)
)
parseConstant("const a ?= 1") should be(
ConstantExpr[Id]("a", toNumber(1), skipIfAlreadyDefined = true)
parseConstant("const A ?= 1") should be(
ConstantExpr[Id]("A", toNumber(1), skipIfAlreadyDefined = true)
)
}
}

View File

@ -14,6 +14,12 @@ class FromSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec.*
"from constants" should "be parsed" in {
FromExpr.nameOrAbAs[Id].parseAll("SOME_CONSTANT").value shouldBe Right((toAb("SOME_CONSTANT"), None))
FromExpr.nameOrAbAs[Id].parseAll("SOME_CONSTANT as SC").value shouldBe Right((toAb("SOME_CONSTANT"), Some(toAb("SC"))))
}
"from expression" should "be parsed" in {
FromExpr.nameOrAbAs[Id].parseAll("Ability").value should be(Right(toAb("Ability") -> None))
FromExpr.nameOrAbAs[Id].parseAll("Ability as Ab").value should be(

View File

@ -0,0 +1,30 @@
package aqua.parser.head
import aqua.AquaSpec
import aqua.parser.expr.AbilityIdExpr
import aqua.parser.lexer.{Literal, Token}
import aqua.parser.lift.LiftParser.Implicits.*
import aqua.types.LiteralType
import cats.Id
import cats.data.NonEmptyList
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class UseSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec.*
"use" should "be parsed" in {
UseFromExpr.p[Id].parseAll("use DECLARE_CONST2 as DC2 from \"declare.aqua\" as Declare").value shouldBe
UseFromExpr(
NonEmptyList.one(Right((toAb("DECLARE_CONST2"), Some(toAb("DC2"))))),
toStr("declare.aqua"),
toAb("Declare"))
UseFromExpr.p[Id].parseAll("use DECLARE_CONST from \"declare.aqua\" as Declare").value shouldBe
UseFromExpr(
NonEmptyList.one(Right((toAb("DECLARE_CONST"), None))),
toStr("declare.aqua"),
toAb("Declare"))
}
}

View File

@ -44,4 +44,12 @@ class TokenSpec extends AnyFlatSpec with Matchers with EitherValues {
|""".stripMargin).value should be(())
}
"name token" should "parse" in {
`name`.parseAll("some_name").value should be("some_name")
}
"NAME token" should "parse" in {
NAME.parseAll("SOME_NAME").value should be("SOME_NAME")
}
}

View File

@ -0,0 +1,26 @@
package aqua.parser.lexer
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
import cats.Id
import cats.data.NonEmptyList
import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class VarLambdaSpec extends AnyFlatSpec with Matchers with EitherValues {
"var lambda" should "parse" in {
val opsP = (s: String) => Name.dotted[Id].parseAll(s).value
opsP("SomeClass.some_val") should be(Name[Id]("SomeClass.some_val"))
opsP("some_val") should be(Name[Id]("some_val"))
opsP("SOME_CONST") should be(Name[Id]("SOME_CONST"))
opsP("SomeClass.SOME_CONST") should be(Name[Id]("SomeClass.SOME_CONST"))
}
}

View File

@ -18,15 +18,12 @@ case class CompilerState[F[_]](
object CompilerState {
type S[F[_]] = State[CompilerState[F], Model]
def init[F[_]](ctx: AquaContext): CompilerState[F] = {
// TODO: should go to Monoid[AquaContext].empty, along with overriden constants
val withLE = ctx.copy(values = ctx.values + ("%last_error%" -> VarModel.lastError))
def init[F[_]](ctx: AquaContext): CompilerState[F] =
CompilerState(
names = NamesState.init[F](withLE),
abilities = AbilitiesState.init[F](withLE),
types = TypesState.init[F](withLE)
names = NamesState.init[F](ctx),
abilities = AbilitiesState.init[F](ctx),
types = TypesState.init[F](ctx)
)
}
implicit def compilerStateMonoid[F[_]]: Monoid[S[F]] = new Monoid[S[F]] {
override def empty: S[F] = State.pure(EmptyModel("compiler state monoid empty"))

View File

@ -58,6 +58,7 @@ object HeaderSem {
def resolve(f: FilenameExpr[S]): ResAC[S] =
imports
.get(f.fileValue)
.map(_.pickDeclared)
.fold[ResAC[S]](
error(f.token, "Cannot resolve the import")
)(validNec)
@ -91,7 +92,7 @@ object HeaderSem {
}
)
)
.reduce
.foldLeft[ResAC[S]](validNec(ctx.pickHeader))(_ |+| _)
// Convert an imported context into a module (ability)
def toModule(ctx: AquaContext, tkn: Token[S], rename: Option[Ability[S]]): ResAC[S] =
@ -103,7 +104,7 @@ object HeaderSem {
tkn,
s"Used module has no `module` header. Please add `module` header or use ... as ModuleName, or switch to import"
)
)(modName => validNec(acm.empty.copy(abilities = Map(modName -> ctx))))
)(modName => validNec(AquaContext.blank.copy(abilities = Map(modName -> ctx))))
// Handler for every header expression, will be combined later
val onExpr: PartialFunction[HeaderExpr[S], Res[S]] = {
@ -154,27 +155,33 @@ object HeaderSem {
// Import, map declarations
resolve(f)
.andThen(getFrom(f, _))
.map(ctx => HeaderSem[S](ctx, (c, _) => validNec(c)))
.map { ctx =>
HeaderSem[S](ctx, (c, _) => validNec(c))
}
case f @ UseExpr(_, asModule) =>
// Import, move into a module scope
resolve(f)
.andThen(toModule(_, f.token, asModule))
.map(fc => HeaderSem[S](fc, (c, _) => validNec(c)))
.map { fc =>
HeaderSem[S](fc, (c, _) => validNec(c))
}
case f @ UseFromExpr(_, _, asModule) =>
// Import, cherry-pick declarations, move to a module scope
resolve(f)
.andThen(getFrom(f, _))
.andThen(toModule(_, f.token, Some(asModule)))
.map(fc => HeaderSem[S](fc, (c, _) => validNec(c)))
.map { fc =>
HeaderSem[S](fc, (c, _) => validNec(c))
}
case ExportExpr(pubs) =>
// Save exports, finally handle them
validNec(
HeaderSem[S](
// Nothing there
acm.empty,
AquaContext.blank,
(ctx, initCtx) =>
pubs
.map(
@ -197,7 +204,7 @@ object HeaderSem {
}
)
)
.foldLeft[ResAC[S]](validNec(ctx.exports.getOrElse(acm.empty)))(_ |+| _)
.foldLeft[ResAC[S]](validNec(ctx.exports.getOrElse(AquaContext.blank)))(_ |+| _)
.map(expCtx => ctx.copy(exports = Some(expCtx)))
)
)

View File

@ -39,7 +39,7 @@ class ValuesAlgebra[F[_], Alg[_]](implicit N: NamesAlgebra[F, Alg], T: TypesAlge
case Some(t) =>
T.resolveLambda(t, ops)
.map(Chain.fromSeq)
.map(VarModel(name.value.replace('.', '_'), t, _))
.map(VarModel(name.value, t, _))
.map(Some(_))
case None =>
Free.pure(None)