diff --git a/parser/src/main/scala/aqua/parser/lexer/ValueToken.scala b/parser/src/main/scala/aqua/parser/lexer/ValueToken.scala index fb1a6e3bc..9c792c7c4 100644 --- a/parser/src/main/scala/aqua/parser/lexer/ValueToken.scala +++ b/parser/src/main/scala/aqua/parser/lexer/ValueToken.scala @@ -32,12 +32,25 @@ case class PropertyToken[F[_]: Comonad]( def mapK[K[_]: Comonad](fk: F ~> K): PropertyToken[K] = copy(value.mapK(fk), properties.map(_.mapK(fk))) + private def isClass(name: String): Boolean = + name.headOption.exists(_.isUpper) + + private def isField(name: String): Boolean = + name.headOption.exists(_.isLower) + + private def isConst(name: String): Boolean = + name.forall(c => !c.isLetter || c.isUpper) + def toCallArrow: Option[CallArrowToken[F]] = value match { case VarToken(name) => val ability = properties.init.traverse { case f @ IntoField(_) => f.value.some case _ => none - }.map(props => name.rename((name.value +: props).mkString("."))) + }.map( + name.value +: _ + ).filter( + _.forall(isClass) + ).map(props => name.rename(props.mkString("."))) (properties.last, ability) match { case (IntoArrow(funcName, args), Some(ability)) => @@ -50,6 +63,40 @@ case class PropertyToken[F[_]: Comonad]( } case _ => none } + + def adjustName: Option[ValueToken[F]] = value match { + case VarToken(name) => + val isAbility = isClass(name.value) && properties.forall { + case f @ IntoField(_) => isField(f.value) + case _ => true + } + + if (!isAbility) none + else { + val props = name.value +: properties.toList.view.map { + case IntoField(name) => name.extract.some + case _ => none + }.takeWhile(_.isDefined).flatten.toList + + val classes = props.takeWhile(isClass) + + val last = props + .drop(classes.length) + .headOption + .filter(name => isConst(name) || isField(name)) + + last.map(last => + val newProps = NonEmptyList.fromList( + properties.toList.drop(classes.length + 1) + ) + val newName = (classes :+ last).mkString(".") + val varToken = VarToken(name.rename(newName)) + + newProps.fold(varToken)(props => PropertyToken(varToken, props)) + ) + } + case _ => none + } } object PropertyToken { diff --git a/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala b/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala index e22e94829..4e794ca55 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala @@ -89,22 +89,28 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit } case prop @ PropertyToken(value, properties) => - prop.toCallArrow.fold( - for { - valueRaw <- valueToRaw(value) - result <- valueRaw.flatTraverse(raw => - properties.foldLeftM(raw.some) { - // Failed to resolve prop case - case (None, _) => none.pure - // Apply next prop case - case (Some(prev), op) => - resolveSingleProperty(prev.`type`, op).map( - _.map(prop => ApplyPropertyRaw(prev, prop)) - ) - } - ) - } yield result - )(valueToRaw) + ( + prop.toCallArrow, + prop.adjustName + ) match { + case (Some(car), _) => valueToRaw(car) + case (_, Some(adjusted)) => valueToRaw(adjusted) + case _ => + for { + valueRaw <- valueToRaw(value) + result <- valueRaw.flatTraverse(raw => + properties.foldLeftM(raw.some) { + // Failed to resolve prop case + case (None, _) => none.pure + // Apply next prop case + case (Some(prev), op) => + resolveSingleProperty(prev.`type`, op).map( + _.map(prop => ApplyPropertyRaw(prev, prop)) + ) + } + ) + } yield result + } case dvt @ NamedValueToken(typeName, fields) => T.resolveType(typeName).flatMap {