Constant expr (#79)

This commit is contained in:
Dima 2021-04-22 16:42:08 +03:00 committed by GitHub
parent 490cb7873b
commit ca8e3bfa40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 311 additions and 84 deletions

View File

@ -1,9 +1,23 @@
import "demo.aqua"
service Demo("demo"):
get4: u64, u64, string -> u64
func one() -> u64:
variable <- Demo.get4()
const bbb = 5
const bbb ?= 2
const ccc = "privet"
const ddd = ccc
const eee = bbb
const asd = eee
-- const ttt = -2
func two(variable: u64) -> u64:
v <- Demo.get4(variable, eee, ddd)
-- if bbb == ttt:
-- Demo.get4(variable, eee, ddd)
-- else:
-- Demo.get4(variable, eee, ddd)
<- variable
func two() -> u64:
variable <- one()
<- variable
func three() -> u64:
variable <- Demo.get4(asd, eee, ddd)
res <- two(variable)
<- variable

View File

@ -2,18 +2,7 @@ package aqua.backend.air
import aqua.model._
import aqua.model.func.Call
import aqua.model.func.body.{
CallArrowTag,
CallServiceTag,
ForTag,
MatchMismatchTag,
NextTag,
OnTag,
OpTag,
ParTag,
SeqTag,
XorTag
}
import aqua.model.func.body._
import cats.Eval
import cats.data.Chain
import cats.free.Cofree

View File

@ -29,9 +29,9 @@ commons
lazy val cli = project
.settings(commons: _*)
.settings(
mainClass in (Compile, run) := Some("aqua.AquaCli"),
mainClass in assembly := Some("aqua.AquaCli"),
assemblyJarName in assembly := "aqua-cli-" + version.value + ".jar",
Compile / run / mainClass := Some("aqua.AquaCli"),
assembly / mainClass := Some("aqua.AquaCli"),
assembly / assemblyJarName := "aqua-cli-" + version.value + ".jar",
libraryDependencies ++= Seq(
"com.monovore" %% "decline" % declineV,
"com.monovore" %% "decline-effect" % declineV,

View File

@ -14,7 +14,7 @@ object Test extends IOApp.Simple {
Paths.get("./aqua-src"),
LazyList(Paths.get("./aqua")),
Paths.get("./target"),
AquaCompiler.AirTarget,
AquaCompiler.TypescriptTarget,
BodyConfig()
)
.map {

View File

@ -26,7 +26,7 @@ case class EmptyFileError(path: Path) extends AquaFileError {
}
case class FileSystemError(err: Throwable) extends Exception(err) with AquaFileError {
override def showForConsole: String = s"File system error"
override def showForConsole: String = s"File system error: ${err.getMessage}"
}
case class Unresolvable(msg: String) extends AquaFileError {

View File

@ -0,0 +1,3 @@
package aqua.model
case class ConstantModel(name: String, value: ValueModel) extends Model

View File

@ -4,21 +4,36 @@ import aqua.model.func.{FuncCallable, FuncModel}
import cats.Monoid
import cats.data.Chain
// TODO make one chain to have order
case class ScriptModel(
funcs: Chain[FuncModel] = Chain.empty,
services: Chain[ServiceModel] = Chain.empty,
types: Chain[TypeModel] = Chain.empty
models: Chain[Model] = Chain.empty
) extends Model {
def resolveFunctions: Chain[FuncCallable] =
case class Acc(
arrows: Map[String, FuncCallable],
values: Map[String, ValueModel]
)
lazy val funcs: Chain[FuncModel] = models.collect { case c: FuncModel => c }
lazy val constants: Chain[ConstantModel] = models.collect { case c: ConstantModel => c }
def resolveFunctions: Chain[FuncCallable] = {
val constantsToName =
constants.map(c => c.name -> c.value).toList.toMap
funcs
.foldLeft((Map.empty[String, FuncCallable], Chain.empty[FuncCallable])) {
case ((funcsAcc, outputAcc), func) =>
val fr = func.captureArrows(funcsAcc)
funcsAcc.updated(func.name, fr) -> outputAcc.append(fr)
.foldLeft(
(
(
Map.empty[String, FuncCallable],
Chain.empty[FuncCallable]
)
)
) { case ((acc, outputAcc), func) =>
val fr = func.capture(acc, constantsToName)
acc -> outputAcc.append(fr)
}
._2
}
}
object ScriptModel {
@ -27,14 +42,17 @@ object ScriptModel {
override def empty: ScriptModel = ScriptModel()
override def combine(x: ScriptModel, y: ScriptModel): ScriptModel =
ScriptModel(x.funcs ++ y.funcs, x.services ++ y.services, x.types ++ y.types)
ScriptModel(
x.models ++ y.models
)
}
// Builds a ScriptModel if given model can be considered as a part of a script
def toScriptPart(m: Model): Option[ScriptModel] = m match {
case fm: FuncModel => Some(ScriptModel(funcs = Chain.one(fm)))
case sm: ServiceModel => Some(ScriptModel(services = Chain.one(sm)))
case tm: TypeModel => Some(ScriptModel(types = Chain.one(tm)))
case fm: FuncModel => Some(ScriptModel(models = Chain.one(fm)))
case sm: ServiceModel => Some(ScriptModel(models = Chain.one(sm)))
case tm: TypeModel => Some(ScriptModel(models = Chain.one(tm)))
case cm: ConstantModel => Some(ScriptModel(models = Chain.one(cm)))
case _ => None
}
}

View File

@ -19,9 +19,38 @@ case class IntoFieldModel(field: String) extends LambdaModel
case class VarModel(name: String, lambda: Chain[LambdaModel] = Chain.empty) extends ValueModel {
def deriveFrom(vm: VarModel): VarModel = vm.copy(lambda = vm.lambda ++ lambda)
override def resolveWith(map: Map[String, ValueModel]): ValueModel = map.get(name) match {
case Some(vv: VarModel) => deriveFrom(vv)
case Some(vv) => vv // TODO check that lambda is empty, otherwise error
case None => this // Should not happen
override def resolveWith(map: Map[String, ValueModel]): ValueModel = {
map.get(name) match {
case Some(vv: VarModel) =>
map.get(vv.name) match {
case Some(n) =>
n match {
/* This case protects from infinite recursion
when similar names are in a body of a function and a call of a function
service Demo("demo"):
get4: u64 -> u64
func two(variable: u64) -> u64:
v <- Demo.get4(variable)
<- variable
func three(v: u64) -> u64:
variable <- Demo.get4(v)
-- here we will try to resolve 'variable' to VarModel('variable')
-- that could cause infinite recursion
res <- two(variable)
<- variable
*/
case vm @ VarModel(nn, _) if nn == name => deriveFrom(vm)
// it couldn't go to a cycle as long as the semantics protects it
case _ => n.resolveWith(map)
}
case _ =>
deriveFrom(vv)
}
case Some(vv) => vv // TODO check that lambda is empty, otherwise error
case None => this // Should not happen
}
}
}

View File

@ -12,7 +12,8 @@ case class FuncCallable(
body: FuncOp,
args: ArgsDef,
ret: Option[Call.Arg],
capturedArrows: Map[String, FuncCallable]
capturedArrows: Map[String, FuncCallable],
capturedValues: Map[String, ValueModel]
) {
def arrowType: ArrowType =
@ -73,7 +74,7 @@ case class FuncCallable(
// Accumulator: all used names are forbidden, if we set any more names -- forbid them as well
(forbiddenNames ++ shouldRename.values ++ treeDefines) ->
// Functions may export variables, so collect them
Map.empty[String, ValueModel]
capturedValues
) {
case ((noNames, resolvedExports), CallArrowTag(fn, c)) if allArrows.contains(fn) =>
// Apply arguments to a function recursion

View File

@ -1,7 +1,7 @@
package aqua.model.func
import aqua.model.func.body.FuncOp
import aqua.model.Model
import aqua.model.{Model, ValueModel}
case class FuncModel(
name: String,
@ -10,7 +10,10 @@ case class FuncModel(
body: FuncOp
) extends Model {
def captureArrows(arrows: Map[String, FuncCallable]): FuncCallable =
FuncCallable(name, body, args, ret, arrows)
def capture(
arrows: Map[String, FuncCallable],
constants: Map[String, ValueModel]
): FuncCallable =
FuncCallable(name, body, args, ret, arrows, constants)
}

View File

@ -1,7 +1,7 @@
package aqua.model.func.body
import aqua.model.{LiteralModel, ValueModel}
import aqua.model.func.Call
import aqua.model.{LiteralModel, ValueModel}
import cats.data.Chain
import cats.free.Cofree

View File

@ -31,6 +31,7 @@ case class ResolveFunc(
callback(name, call),
args,
ret,
Map.empty,
Map.empty
)
}
@ -55,7 +56,8 @@ case class ResolveFunc(
None,
func.args.arrowArgs.collect { case ArgDef.Arrow(argName, arrowType) =>
argName -> arrowToCallback(argName, arrowType)
}.toList.toMap
}.toList.toMap,
func.capturedValues
)
def resolve(func: FuncCallable, funcArgName: String = "_func"): Eval[FuncOp] =

View File

@ -1,9 +1,6 @@
package aqua.model.transform
import aqua.model.func.body.{CallArrowTag, CallServiceTag, FuncOp}
import aqua.model.func.{ArgsDef, Call, FuncCallable}
import aqua.model.{LiteralModel, Node, VarModel}
import aqua.types.ScalarType
import aqua.model.Node
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

View File

@ -1,8 +1,8 @@
package aqua.model.transform
import aqua.model.{LiteralModel, Node, VarModel}
import aqua.model.func.{ArgsDef, Call, FuncCallable}
import aqua.model.func.body.{CallArrowTag, CallServiceTag, FuncOp}
import aqua.model.func.{ArgsDef, Call, FuncCallable}
import aqua.model.{LiteralModel, Node, VarModel}
import aqua.types.ScalarType
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
@ -20,6 +20,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
FuncOp(on(otherPeer, Nil, call(1))),
ArgsDef.empty,
Some(Call.Arg(ret, ScalarType.string)),
Map.empty,
Map.empty
)
@ -57,6 +58,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
FuncOp(seq(call(0), on(otherPeer, Nil, call(1)))),
ArgsDef.empty,
Some(Call.Arg(ret, ScalarType.string)),
Map.empty,
Map.empty
)
@ -105,6 +107,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
FuncOp(Node(CallServiceTag(LiteralModel("\"srv1\""), "foo", Call(Nil, Some("v")), None))),
ArgsDef.empty,
Some(Call.Arg(VarModel("v"), ScalarType.string)),
Map.empty,
Map.empty
)
@ -116,7 +119,8 @@ class TransformSpec extends AnyFlatSpec with Matchers {
),
ArgsDef.empty,
Some(Call.Arg(VarModel("v"), ScalarType.string)),
Map("callable" -> f1)
Map("callable" -> f1),
Map.empty
)
val bc = BodyConfig(wrapWithXor = false)

View File

@ -20,7 +20,7 @@ object Ast {
type Head[F[_]] = Cofree[Chain, HeaderExpr[F]]
def treeExprs: List[Expr.Companion] =
ServiceExpr :: AliasExpr :: DataStructExpr :: FuncExpr :: Nil
ServiceExpr :: AliasExpr :: DataStructExpr :: ConstantExpr :: FuncExpr :: Nil
def headExprs: List[HeaderExpr.Companion] =
ImportExpr :: Nil

View File

@ -0,0 +1,21 @@
package aqua.parser.expr
import aqua.parser.Expr
import aqua.parser.lexer.Token._
import aqua.parser.lexer.{Name, Value}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.parse.{Parser => P}
case class AssignmentExpr[F[_]](
variable: Name[F],
value: Value[F]
) extends Expr[F]
object AssignmentExpr extends Expr.Leaf {
override def p[F[_]: LiftParser: Comonad]: P[AssignmentExpr[F]] =
((Name.p[F] <* ` = `).with1 ~ Value.`value`).map { case (variable, value) =>
AssignmentExpr(variable, value)
}
}

View File

@ -20,8 +20,9 @@ object CallArrowExpr extends Expr.Leaf {
((Name.p[F] <* ` <- `).backtrack.?.with1 ~
((Ability.ab[F] <* `.`).?.with1 ~
Name.p[F] ~
comma0(Value.`value`[F]).between(`(`, `)`))).map { case (variable, ((ability, funcName), args)) =>
CallArrowExpr(variable, ability, funcName, args)
comma0(Value.`value`[F]).between(`(`, `)`))).map {
case (variable, ((ability, funcName), args)) =>
CallArrowExpr(variable, ability, funcName, args)
}
}

View File

@ -0,0 +1,25 @@
package aqua.parser.expr
import aqua.parser.Expr
import aqua.parser.lexer.Token._
import aqua.parser.lexer.{Name, Value}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.parse.{Parser => P}
case class ConstantExpr[F[_]](
name: Name[F],
value: Value[F],
skipIfAlreadyDefined: Boolean
) extends Expr[F]
object ConstantExpr extends Expr.Leaf {
override def p[F[_]: LiftParser: Comonad]: P[ConstantExpr[F]] = {
((((`const` *> ` ` *> Name
.p[F] <* ` `) ~ `?`.?).with1 <* `=` <* ` `) ~ Value.`value`).map {
case ((name, mark), value) =>
ConstantExpr(name, value, mark.nonEmpty)
}
}
}

View File

@ -20,6 +20,7 @@ object Token {
private val nl = Set('\n', '\r')
val ` ` : P[String] = P.charsWhile(fSpaces)
val `const`: P[Unit] = P.string("const")
val `data`: P[Unit] = P.string("data")
val `import`: P[Unit] = P.string("import")
val `use`: P[Unit] = P.string("use")
@ -62,6 +63,9 @@ object Token {
val `)` : P[Unit] = ` `.?.with1 *> P.char(')') <* ` `.?
val ` -> ` : P[Unit] = ` `.?.with1 *> P.string("->") <* ` `.?
val ` <- ` : P[Unit] = (` `.?.with1 *> P.string("<-") <* ` `.?).backtrack
val `=` : P[Unit] = P.string("=")
val ` = ` : P[Unit] = (` `.?.with1 *> P.string("=") <* ` `.?).backtrack
val `?` : P[Unit] = P.string("?")
val `<-` : P[Unit] = P.string("<-").backtrack
def comma[T](p: P[T]): P[NonEmptyList[T]] =

View File

@ -4,7 +4,9 @@ import aqua.parser.expr.{
AbilityIdExpr,
AliasExpr,
ArrowTypeExpr,
AssignmentExpr,
CallArrowExpr,
ConstantExpr,
DataStructExpr,
ElseOtherwiseExpr,
FieldTypeExpr,
@ -101,6 +103,12 @@ trait AquaSpec extends EitherValues {
def parseReturn(str: String): ReturnExpr[Id] =
ReturnExpr.p[Id].parseAll(str).value
def parseAssign(str: String): AssignmentExpr[Id] =
AssignmentExpr.p[Id].parseAll(str).value
def parseConstant(str: String): ConstantExpr[Id] =
ConstantExpr.p[Id].parseAll(str).value
def parseService(str: String): ServiceExpr[Id] =
ServiceExpr.p[Id].parseAll(str).value

View File

@ -0,0 +1,33 @@
package aqua.parser
import aqua.AquaSpec
import aqua.parser.expr.{AssignmentExpr, ConstantExpr}
import cats.Id
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class AssignmentExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec._
"assign" should "be parsed" in {
parseAssign("a = \"b\"") should be(
AssignmentExpr[Id]("a", toStr("b"))
)
parseAssign("a = b") should be(
AssignmentExpr[Id]("a", toVar("b"))
)
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 = true)
)
}
}

View File

@ -5,7 +5,7 @@ import aqua.parser.Ast.parser
import aqua.parser.expr._
import aqua.parser.lexer.{ArrowTypeToken, BasicTypeToken, EqOp}
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
import aqua.types.ScalarType.{bool, string, u32, u64, u8}
import aqua.types.ScalarType._
import cats.Id
import cats.data.Chain
import cats.free.Cofree
@ -89,7 +89,6 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
val tree = FuncExpr.ast[Id](Indent()).parseAll(script).value
val funcBody = checkHeadGetTail(tree, FuncExpr("a", Nil, None, None), 1).toList
println("body: " + funcBody)
val ifBody =
checkHeadGetTail(

View File

@ -1,7 +1,7 @@
package aqua.parser
import aqua.AquaSpec
import aqua.parser.expr.{ParExpr, ReturnExpr}
import aqua.parser.expr.ReturnExpr
import cats.Id
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
@ -9,7 +9,7 @@ import org.scalatest.matchers.should.Matchers
class ReturnExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec._
"on" should "be parsed" in {
"return" should "be parsed" in {
parseReturn("<- true") should be(
ReturnExpr[Id](toBool(true))
)

View File

@ -20,6 +20,7 @@ object ExprSem {
expr match {
case expr: AbilityIdExpr[F] => new AbilityIdSem(expr).program[G]
case expr: AliasExpr[F] => new AliasSem(expr).program[G]
case expr: ConstantExpr[F] => new ConstantSem(expr).program[G]
case expr: ArrowTypeExpr[F] => new ArrowTypeSem(expr).program[G]
case expr: CallArrowExpr[F] => new CallArrowSem(expr).program[G]
case expr: DataStructExpr[F] => new DataStructSem(expr).program[G]

View File

@ -1,8 +1,8 @@
package aqua.semantics.expr
import aqua.model.Model
import aqua.model.func.Call
import aqua.model.func.body.{CallArrowTag, CallServiceTag, FuncOp}
import aqua.model.Model
import aqua.parser.expr.CallArrowExpr
import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra
@ -11,9 +11,9 @@ import aqua.semantics.rules.names.NamesAlgebra
import aqua.semantics.rules.types.TypesAlgebra
import aqua.types.ArrowType
import cats.free.Free
import cats.syntax.apply._
import cats.syntax.flatMap._
import cats.syntax.functor._
import cats.syntax.apply._
class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {

View File

@ -0,0 +1,42 @@
package aqua.semantics.expr
import aqua.model.{ConstantModel, Model}
import aqua.parser.expr.ConstantExpr
import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.names.NamesAlgebra
import aqua.semantics.rules.types.TypesAlgebra
import cats.free.Free
import cats.syntax.functor._
class ConstantSem[F[_]](val expr: ConstantExpr[F]) extends AnyVal {
def program[Alg[_]](implicit
V: ValuesAlgebra[F, Alg],
N: NamesAlgebra[F, Alg],
T: TypesAlgebra[F, Alg]
): Prog[Alg, Model] = {
for {
defined <- N.constantDefined(expr.name)
t <- V.resolveType(expr.value)
model <- (defined, t, expr.skipIfAlreadyDefined) match {
case (Some(definedType), Some(actualType), true) =>
T.ensureTypeMatches(expr.value, definedType, actualType).map {
case true =>
Model.empty(s"Constant with name ${expr.name} was already defined, skipping")
case false =>
Model.error(s"Constant with name ${expr.name} was defined with different type")
}
case (Some(_), _, _) =>
Free.pure[Alg, Model](Model.error(s"Name '${expr.name.value}' was already defined"))
case (_, None, _) =>
Free.pure[Alg, Model](Model.error(s"There is no such variable ${expr.value}"))
case (_, Some(t), _) =>
N.defineConstant(expr.name, t) as (ConstantModel(
expr.name.value,
ValuesAlgebra.valueToModel(expr.value)
): Model)
}
} yield model
}
}

View File

@ -1,17 +1,16 @@
package aqua.semantics.expr
import aqua.model.func.FuncModel
import aqua.model.{Model, ScriptModel, ServiceModel, TypeModel}
import aqua.model.{Model, ScriptModel}
import aqua.parser.expr.RootExpr
import aqua.semantics.Prog
import cats.data.Chain
import cats.free.Free
class RootSem[F[_]](val expr: RootExpr[F]) extends AnyVal {
def program[Alg[_]]: Prog[Alg, Model] =
Prog.after {
case sm: ScriptModel => Free.pure[Alg, Model](sm)
case sm: ScriptModel =>
Free.pure[Alg, Model](sm)
case m =>
Free.pure[Alg, Model](
ScriptModel

View File

@ -1,9 +1,9 @@
package aqua.semantics.rules
import aqua.model.{IntoArrayModel, IntoFieldModel, LambdaModel, LiteralModel, ValueModel, VarModel}
import aqua.model._
import aqua.parser.lexer._
import aqua.semantics.rules.names.NamesAlgebra
import aqua.semantics.rules.types.TypesAlgebra
import aqua.parser.lexer.{IntoArray, IntoField, LambdaOp, Literal, Token, Value, VarLambda}
import aqua.types.{ArrowType, LiteralType, Type}
import cats.data.Chain
import cats.free.Free

View File

@ -6,10 +6,12 @@ import aqua.types.{ArrowType, Type}
sealed trait NameOp[F[_], T]
case class ReadName[F[_]](name: Name[F]) extends NameOp[F, Option[Type]]
case class ConstantDefined[F[_]](name: Name[F]) extends NameOp[F, Option[Type]]
case class ReadArrow[F[_]](name: Name[F]) extends NameOp[F, Option[ArrowType]]
case class DefineName[F[_]](name: Name[F], `type`: Type) extends NameOp[F, Boolean]
case class DefineConstant[F[_]](name: Name[F], `type`: Type) extends NameOp[F, Boolean]
case class DefineArrow[F[_]](name: Name[F], gen: ArrowType, isRoot: Boolean)
extends NameOp[F, Boolean]

View File

@ -1,6 +1,6 @@
package aqua.semantics.rules.names
import aqua.parser.lexer.{Name, Token}
import aqua.parser.lexer.{Literal, Name, Token, Value}
import aqua.types.{ArrowType, Type}
import cats.InjectK
import cats.free.Free
@ -10,12 +10,18 @@ class NamesAlgebra[F[_], Alg[_]](implicit V: InjectK[NameOp[F, *], Alg]) {
def read(name: Name[F]): Free[Alg, Option[Type]] =
Free.liftInject[Alg](ReadName(name))
def constantDefined(name: Name[F]): Free[Alg, Option[Type]] =
Free.liftInject[Alg](ConstantDefined(name))
def readArrow(name: Name[F]): Free[Alg, Option[ArrowType]] =
Free.liftInject[Alg](ReadArrow(name))
def define(name: Name[F], `type`: Type): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineName(name, `type`))
def defineConstant(name: Name[F], `type`: Type): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineConstant(name, `type`))
def defineArrow(name: Name[F], gen: ArrowType, isRoot: Boolean): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineArrow(name, gen, isRoot))

View File

@ -2,12 +2,12 @@ package aqua.semantics.rules.names
import aqua.semantics.rules.{ReportError, StackInterpreter}
import aqua.types.{ArrowType, Type}
import cats.data.State
import cats.data.{OptionT, State}
import cats.syntax.flatMap._
import cats.syntax.functor._
import cats.~>
import monocle.Lens
import monocle.macros.GenLens
import cats.syntax.functor._
import cats.syntax.flatMap._
class NamesInterpreter[F[_], X](implicit lens: Lens[X, NamesState[F]], error: ReportError[F, X])
extends StackInterpreter[F, X, NamesState[F], NamesState.Frame[F]](
@ -16,12 +16,15 @@ class NamesInterpreter[F[_], X](implicit lens: Lens[X, NamesState[F]], error: Re
def readName(name: String): S[Option[Type]] =
getState.map { st =>
st.stack.collectFirst {
st.constants.get(name) orElse st.stack.collectFirst {
case frame if frame.names.contains(name) => frame.names(name)
case frame if frame.arrows.contains(name) => frame.arrows(name)
} orElse st.rootArrows.get(name)
}
def constantDefined(name: String): S[Option[Type]] =
getState.map(_.constants.get(name))
def readArrow(name: String): S[Option[ArrowType]] =
getState.map { st =>
st.stack.flatMap(_.arrows.get(name)).headOption orElse st.rootArrows.get(name)
@ -30,13 +33,19 @@ class NamesInterpreter[F[_], X](implicit lens: Lens[X, NamesState[F]], error: Re
override def apply[A](fa: NameOp[F, A]): State[X, A] =
(fa match {
case rn: ReadName[F] =>
readName(rn.name.value).flatTap {
case Some(_) => State.pure(())
case None =>
getState.flatMap(st =>
report(rn.name, "Undefined name, available: " + st.allNames.mkString(", "))
)
}
OptionT(constantDefined(rn.name.value))
.orElseF(readName(rn.name.value))
.value
.flatTap {
case Some(_) => State.pure(())
case None =>
getState.flatMap(st =>
report(rn.name, "Undefined name, available: " + st.allNames.mkString(", "))
)
}
case rn: ConstantDefined[F] =>
constantDefined(rn.name.value)
case ra: ReadArrow[F] =>
readArrow(ra.name.value).flatMap {
case Some(g) => State.pure(Option(g))
@ -47,6 +56,17 @@ class NamesInterpreter[F[_], X](implicit lens: Lens[X, NamesState[F]], error: Re
)
}
case dc: DefineConstant[F] =>
readName(dc.name.value).flatMap {
case Some(_) =>
report(dc.name, "This name was already defined in the scope").as(false)
case None =>
modify(st =>
st.copy(
constants = st.constants.updated(dc.name.value, dc.`type`)
)
).as(true)
}
case dn: DefineName[F] =>
readName(dn.name.value).flatMap {
case Some(_) =>

View File

@ -1,17 +1,22 @@
package aqua.semantics.rules.names
import aqua.parser.lexer.{Name, Token}
import aqua.parser.lexer.{Name, Token, Value}
import aqua.types.{ArrowType, Type}
import cats.kernel.Monoid
case class NamesState[F[_]](
stack: List[NamesState.Frame[F]] = Nil,
rootArrows: Map[String, ArrowType] = Map.empty,
constants: Map[String, Type] = Map.empty[String, Type],
definitions: Map[String, Name[F]] = Map.empty[String, Name[F]]
) {
def allNames: LazyList[String] =
LazyList.from(stack).flatMap(s => s.names.keys ++ s.arrows.keys).appendedAll(rootArrows.keys)
LazyList
.from(stack)
.flatMap(s => s.names.keys ++ s.arrows.keys)
.appendedAll(rootArrows.keys)
.appendedAll(constants.keys)
def allArrows: LazyList[String] =
LazyList.from(stack).flatMap(_.arrows.keys).appendedAll(rootArrows.keys)
@ -37,7 +42,8 @@ object NamesState {
NamesState(
stack = Nil,
rootArrows = x.rootArrows ++ y.rootArrows,
definitions = x.definitions ++ y.definitions
definitions = x.definitions ++ y.definitions,
constants = x.constants ++ y.constants
)
}
}