mirror of
https://github.com/fluencelabs/aqua.git
synced 2025-04-25 15:02:14 +00:00
fix(compiler): Fix math ops for u64
[fixes LNG-204] (#811)
This commit is contained in:
parent
cb539f1332
commit
50ba194b86
@ -103,7 +103,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val const = ctx.allValues.get("X")
|
val const = ctx.allValues.get("X")
|
||||||
const.nonEmpty should be(true)
|
const.nonEmpty should be(true)
|
||||||
const.get should be(LiteralModel("5", LiteralType.number))
|
const.get should be(LiteralModel.number(5))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
aqua Math
|
||||||
|
|
||||||
|
export test1, test2, testI16, testI32, testI64, testU64
|
||||||
|
|
||||||
func test1() -> u64:
|
func test1() -> u64:
|
||||||
res = 1 + 2 - 3 * 5 - 2 * 3 / 2 + 5
|
res = 1 + 2 - 3 * 5 - 2 * 3 / 2 + 5
|
||||||
@ -6,3 +9,71 @@ func test1() -> u64:
|
|||||||
func test2() -> u64:
|
func test2() -> u64:
|
||||||
res = 2 ** 2 ** (2 * 2 - 2) + 2 - 3 * 5 - 2 * 3 / 2 + 5 + (4 % 2 - 2)
|
res = 2 ** 2 ** (2 * 2 - 2) + 2 - 3 * 5 - 2 * 3 / 2 + 5 + (4 % 2 - 2)
|
||||||
<- res
|
<- res
|
||||||
|
|
||||||
|
func getI8() -> i8:
|
||||||
|
<- -8
|
||||||
|
|
||||||
|
func getI16() -> i16:
|
||||||
|
<- -16
|
||||||
|
|
||||||
|
func getI32() -> i32:
|
||||||
|
<- -32
|
||||||
|
|
||||||
|
func getI64() -> i64:
|
||||||
|
<- -64
|
||||||
|
|
||||||
|
func getU8() -> u8:
|
||||||
|
<- 8
|
||||||
|
|
||||||
|
func getU16() -> u16:
|
||||||
|
<- 16
|
||||||
|
|
||||||
|
func getU32() -> u32:
|
||||||
|
<- 32
|
||||||
|
|
||||||
|
func getU64() -> u64:
|
||||||
|
<- 64
|
||||||
|
|
||||||
|
func testI16(peer: string) -> []i16:
|
||||||
|
res: *i16
|
||||||
|
|
||||||
|
on peer:
|
||||||
|
res <<- getI16() + getI16()
|
||||||
|
res <<- getI8() * getU8()
|
||||||
|
res <<- getI8() % getI16()
|
||||||
|
res <<- getI16() - getI8()
|
||||||
|
|
||||||
|
<- res
|
||||||
|
|
||||||
|
func testI32(peer: string) -> []i32:
|
||||||
|
res: *i32
|
||||||
|
|
||||||
|
on peer:
|
||||||
|
res <<- getI32() + getU16()
|
||||||
|
res <<- getI16() * getU16()
|
||||||
|
res <<- getI8() % getU16()
|
||||||
|
res <<- getI16() - getI32()
|
||||||
|
|
||||||
|
<- res
|
||||||
|
|
||||||
|
func testI64(peer: string) -> []i64:
|
||||||
|
res: *i64
|
||||||
|
|
||||||
|
on peer:
|
||||||
|
res <<- getI32() + getU32()
|
||||||
|
res <<- getI16() * getU32()
|
||||||
|
res <<- getI64() % getI64()
|
||||||
|
res <<- getU8() - getI64()
|
||||||
|
|
||||||
|
<- res
|
||||||
|
|
||||||
|
func testU64(peer: string) -> []u64:
|
||||||
|
res: *u64
|
||||||
|
|
||||||
|
on peer:
|
||||||
|
res <<- getU32() + getU64()
|
||||||
|
res <<- getU64() * getU64()
|
||||||
|
res <<- getU64() % getU16()
|
||||||
|
res <<- getU8() - getU64()
|
||||||
|
|
||||||
|
<- res
|
@ -54,7 +54,7 @@ import {
|
|||||||
} from '../examples/collectionSugarCall.js';
|
} from '../examples/collectionSugarCall.js';
|
||||||
import {funcsCall} from '../examples/funcsCall.js';
|
import {funcsCall} from '../examples/funcsCall.js';
|
||||||
import {nestedDataCall} from '../examples/nestedDataCall.js';
|
import {nestedDataCall} from '../examples/nestedDataCall.js';
|
||||||
import {mathTest1Call, mathTest2Call} from '../examples/mathCall.js';
|
import {mathTest1Call, mathTest2Call, mathTestI16Call, mathTestI32Call, mathTestI64Call, mathTestU64Call} from '../examples/mathCall.js';
|
||||||
import {lng58Bug} from '../compiled/examples/closures.js';
|
import {lng58Bug} from '../compiled/examples/closures.js';
|
||||||
import {config, isEphemeral} from '../config.js';
|
import {config, isEphemeral} from '../config.js';
|
||||||
import {bugLng79Call} from "../examples/canonCall.js";
|
import {bugLng79Call} from "../examples/canonCall.js";
|
||||||
@ -275,6 +275,30 @@ describe('Testing examples', () => {
|
|||||||
expect(res).toEqual(3);
|
expect(res).toEqual(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('math.aqua test I16', async () => {
|
||||||
|
let res = await mathTestI16Call(relay1.peerId);
|
||||||
|
|
||||||
|
expect(res).toEqual([-32, -64, -8, -8]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('math.aqua test I32', async () => {
|
||||||
|
let res = await mathTestI32Call(relay1.peerId);
|
||||||
|
|
||||||
|
expect(res).toEqual([-16, -256, -8, 16]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('math.aqua test I64', async () => {
|
||||||
|
let res = await mathTestI64Call(relay1.peerId);
|
||||||
|
|
||||||
|
expect(res).toEqual([0, -512, 0, 72]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('math.aqua test U64', async () => {
|
||||||
|
let res = await mathTestU64Call(relay1.peerId);
|
||||||
|
|
||||||
|
expect(res).toEqual([96, 4096, 0, -56]);
|
||||||
|
});
|
||||||
|
|
||||||
it('multiReturn.aqua', async () => {
|
it('multiReturn.aqua', async () => {
|
||||||
let multiReturnResult = await multiReturnCall();
|
let multiReturnResult = await multiReturnCall();
|
||||||
expect(multiReturnResult).toEqual([['some-str', 'random-str', 'some-str'], 5, 'some-str', [1, 2], null, 10]);
|
expect(multiReturnResult).toEqual([['some-str', 'random-str', 'some-str'], 5, 'some-str', [1, 2], null, 10]);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {test1, test2} from '../compiled/examples/math.js';
|
import {test1, test2, testI16, testI32, testI64, testU64} from '../compiled/examples/math.js';
|
||||||
|
|
||||||
export async function mathTest1Call(): Promise<number> {
|
export async function mathTest1Call(): Promise<number> {
|
||||||
return await test1();
|
return await test1();
|
||||||
@ -7,3 +7,19 @@ export async function mathTest1Call(): Promise<number> {
|
|||||||
export async function mathTest2Call(): Promise<number> {
|
export async function mathTest2Call(): Promise<number> {
|
||||||
return await test2();
|
return await test2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function mathTestI16Call(peer: string): Promise<number[]> {
|
||||||
|
return await testI16(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function mathTestI32Call(peer: string): Promise<number[]> {
|
||||||
|
return await testI32(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function mathTestI64Call(peer: string): Promise<number[]> {
|
||||||
|
return await testI64(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function mathTestU64Call(peer: string): Promise<number[]> {
|
||||||
|
return await testU64(peer);
|
||||||
|
}
|
@ -121,9 +121,9 @@ case class LiteralRaw(value: String, baseType: Type) extends ValueRaw {
|
|||||||
object LiteralRaw {
|
object LiteralRaw {
|
||||||
def quote(value: String): LiteralRaw = LiteralRaw("\"" + value + "\"", LiteralType.string)
|
def quote(value: String): LiteralRaw = LiteralRaw("\"" + value + "\"", LiteralType.string)
|
||||||
|
|
||||||
def number(value: Int): LiteralRaw = LiteralRaw(value.toString, LiteralType.number)
|
def number(value: Int): LiteralRaw = LiteralRaw(value.toString, LiteralType.forInt(value))
|
||||||
|
|
||||||
val Zero: LiteralRaw = LiteralRaw("0", LiteralType.number)
|
val Zero: LiteralRaw = number(0)
|
||||||
|
|
||||||
val True: LiteralRaw = LiteralRaw("true", LiteralType.bool)
|
val True: LiteralRaw = LiteralRaw("true", LiteralType.bool)
|
||||||
val False: LiteralRaw = LiteralRaw("false", LiteralType.bool)
|
val False: LiteralRaw = LiteralRaw("false", LiteralType.bool)
|
||||||
@ -162,11 +162,14 @@ case class MakeStructRaw(fields: NonEmptyMap[String, ValueRaw], structType: Stru
|
|||||||
copy(fields = fields.map(_.renameVars(map)))
|
copy(fields = fields.map(_.renameVars(map)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case class AbilityRaw(fieldsAndArrows: NonEmptyMap[String, ValueRaw], abilityType: AbilityType) extends ValueRaw {
|
case class AbilityRaw(fieldsAndArrows: NonEmptyMap[String, ValueRaw], abilityType: AbilityType)
|
||||||
|
extends ValueRaw {
|
||||||
|
|
||||||
override def baseType: Type = abilityType
|
override def baseType: Type = abilityType
|
||||||
|
|
||||||
override def map(f: ValueRaw => ValueRaw): ValueRaw = f(copy(fieldsAndArrows = fieldsAndArrows.map(f)))
|
override def map(f: ValueRaw => ValueRaw): ValueRaw = f(
|
||||||
|
copy(fieldsAndArrows = fieldsAndArrows.map(f))
|
||||||
|
)
|
||||||
|
|
||||||
override def varNames: Set[String] = {
|
override def varNames: Set[String] = {
|
||||||
fieldsAndArrows.toSortedMap.values.flatMap(_.varNames).toSet
|
fieldsAndArrows.toSortedMap.values.flatMap(_.varNames).toSet
|
||||||
|
@ -70,7 +70,7 @@ object LiteralModel {
|
|||||||
|
|
||||||
def quote(str: String): LiteralModel = LiteralModel(s"\"$str\"", LiteralType.string)
|
def quote(str: String): LiteralModel = LiteralModel(s"\"$str\"", LiteralType.string)
|
||||||
|
|
||||||
def number(n: Int): LiteralModel = LiteralModel(n.toString, LiteralType.number)
|
def number(n: Int): LiteralModel = LiteralModel(n.toString, LiteralType.forInt(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait PropertyModel {
|
sealed trait PropertyModel {
|
||||||
|
@ -80,10 +80,7 @@ object ModelBuilder {
|
|||||||
ValueModel.fromRaw(bc.errorHandlingSrvId),
|
ValueModel.fromRaw(bc.errorHandlingSrvId),
|
||||||
bc.errorFuncName,
|
bc.errorFuncName,
|
||||||
CallRes(
|
CallRes(
|
||||||
ValueModel.lastError :: LiteralModel(
|
ValueModel.lastError :: LiteralModel.number(i) :: Nil,
|
||||||
i.toString,
|
|
||||||
LiteralType.number
|
|
||||||
) :: Nil,
|
|
||||||
None
|
None
|
||||||
),
|
),
|
||||||
on
|
on
|
||||||
|
@ -34,7 +34,7 @@ case class LiteralToken[F[_]: Comonad](valueToken: F[String], ts: LiteralType)
|
|||||||
|
|
||||||
def value: String = valueToken.extract
|
def value: String = valueToken.extract
|
||||||
|
|
||||||
override def toString: String = s"$value"
|
override def toString: String = s"$value:$ts"
|
||||||
}
|
}
|
||||||
|
|
||||||
case class CollectionToken[F[_]: Comonad](
|
case class CollectionToken[F[_]: Comonad](
|
||||||
@ -96,7 +96,8 @@ object CallArrowToken {
|
|||||||
Name.p
|
Name.p
|
||||||
~ abilities().? ~ comma0(ValueToken.`value`.surroundedBy(`/s*`))
|
~ abilities().? ~ comma0(ValueToken.`value`.surroundedBy(`/s*`))
|
||||||
.between(` `.?.with1 *> `(` <* `/s*`, `/s*` *> `)`)
|
.between(` `.?.with1 *> `(` <* `/s*`, `/s*` *> `)`)
|
||||||
).map { case ((n, ab), args) =>
|
)
|
||||||
|
.map { case ((n, ab), args) =>
|
||||||
CallBraces(n, ab.map(_.toList).getOrElse(Nil), args)
|
CallBraces(n, ab.map(_.toList).getOrElse(Nil), args)
|
||||||
}
|
}
|
||||||
.withContext(
|
.withContext(
|
||||||
@ -171,6 +172,11 @@ object InfixToken {
|
|||||||
|
|
||||||
def p: P[Unit] = P.string(symbol)
|
def p: P[Unit] = P.string(symbol)
|
||||||
|
|
||||||
|
object Op {
|
||||||
|
val math: List[Op] = List(Pow, Mul, Div, Rem, Add, Sub)
|
||||||
|
val compare: List[Op] = List(Gt, Gte, Lt, Lte)
|
||||||
|
}
|
||||||
|
|
||||||
private def opsParser(ops: List[Op]): P[(Span, Op)] =
|
private def opsParser(ops: List[Op]): P[(Span, Op)] =
|
||||||
P.oneOf(ops.map(op => op.p.lift.map(s => s.as(op))))
|
P.oneOf(ops.map(op => op.p.lift.map(s => s.as(op))))
|
||||||
|
|
||||||
@ -351,7 +357,7 @@ object ValueToken {
|
|||||||
(minus.?.with1 ~ Numbers.nonNegativeIntString).lift.map(fu =>
|
(minus.?.with1 ~ Numbers.nonNegativeIntString).lift.map(fu =>
|
||||||
fu.extract match {
|
fu.extract match {
|
||||||
case (Some(_), n) ⇒ LiteralToken(fu.as(s"-$n"), LiteralType.signed)
|
case (Some(_), n) ⇒ LiteralToken(fu.as(s"-$n"), LiteralType.signed)
|
||||||
case (None, n) ⇒ LiteralToken(fu.as(n), LiteralType.number)
|
case (None, n) ⇒ LiteralToken(fu.as(n), LiteralType.unsigned)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ import aqua.parser.head.{FromExpr, UseFromExpr}
|
|||||||
import aqua.parser.lexer.*
|
import aqua.parser.lexer.*
|
||||||
import aqua.parser.lexer.Token.LiftToken
|
import aqua.parser.lexer.Token.LiftToken
|
||||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||||
import aqua.types.LiteralType.{bool, number, string}
|
import aqua.types.LiteralType.{bool, number, signed, string, unsigned}
|
||||||
import aqua.types.{LiteralType, ScalarType}
|
import aqua.types.{LiteralType, ScalarType}
|
||||||
import cats.{~>, Id}
|
import cats.{~>, Id}
|
||||||
import org.scalatest.EitherValues
|
import org.scalatest.EitherValues
|
||||||
@ -60,7 +60,9 @@ object AquaSpec {
|
|||||||
implicit def toVarIndex(name: String, idx: Int): VarToken[Id] =
|
implicit def toVarIndex(name: String, idx: Int): VarToken[Id] =
|
||||||
VarToken[Id](toName(name), IntoIndex[Id](toNumber(idx).unit, Some(toNumber(idx))) :: Nil)
|
VarToken[Id](toName(name), IntoIndex[Id](toNumber(idx).unit, Some(toNumber(idx))) :: Nil)
|
||||||
implicit def toLiteral(name: String, t: LiteralType): LiteralToken[Id] = LiteralToken[Id](name, t)
|
implicit def toLiteral(name: String, t: LiteralType): LiteralToken[Id] = LiteralToken[Id](name, t)
|
||||||
implicit def toNumber(n: Int): LiteralToken[Id] = LiteralToken[Id](n.toString, number)
|
|
||||||
|
implicit def toNumber(n: Int): LiteralToken[Id] =
|
||||||
|
LiteralToken[Id](n.toString, LiteralType.forInt(n))
|
||||||
implicit def toBool(n: Boolean): LiteralToken[Id] = LiteralToken[Id](n.toString, bool)
|
implicit def toBool(n: Boolean): LiteralToken[Id] = LiteralToken[Id](n.toString, bool)
|
||||||
implicit def toStr(n: String): LiteralToken[Id] = LiteralToken[Id]("\"" + n + "\"", string)
|
implicit def toStr(n: String): LiteralToken[Id] = LiteralToken[Id]("\"" + n + "\"", string)
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class AbilityIdExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
|||||||
)
|
)
|
||||||
|
|
||||||
parseAbId("Ab 1") should be(
|
parseAbId("Ab 1") should be(
|
||||||
AbilityIdExpr[Id](toNamedType("Ab"), LiteralToken[Id]("1", LiteralType.number))
|
AbilityIdExpr[Id](toNamedType("Ab"), toNumber(1))
|
||||||
)
|
)
|
||||||
|
|
||||||
parseAbId("Ab a.id") should be(
|
parseAbId("Ab a.id") should be(
|
||||||
|
@ -16,15 +16,13 @@ class AbilityValueExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
|||||||
import AquaSpec.*
|
import AquaSpec.*
|
||||||
|
|
||||||
private def parseAndCheckAbility(str: String) = {
|
private def parseAndCheckAbility(str: String) = {
|
||||||
val one = LiteralToken[Id]("1", LiteralType.number)
|
|
||||||
|
|
||||||
parseData(
|
parseData(
|
||||||
str
|
str
|
||||||
) should be(
|
) should be(
|
||||||
NamedValueToken(
|
NamedValueToken(
|
||||||
NamedTypeToken[Id]("AbilityA"),
|
NamedTypeToken[Id]("AbilityA"),
|
||||||
NonEmptyMap.of(
|
NonEmptyMap.of(
|
||||||
"v1" -> one,
|
"v1" -> toNumber(1),
|
||||||
"f1" -> VarToken(Name[Id]("input"), IntoField[Id]("arrow") :: Nil)
|
"f1" -> VarToken(Name[Id]("input"), IntoField[Id]("arrow") :: Nil)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -36,8 +34,7 @@ class AbilityValueExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"multiline line struct value" should "be parsed" in {
|
"multiline line struct value" should "be parsed" in {
|
||||||
parseAndCheckAbility(
|
parseAndCheckAbility("""AbilityA(v1 = 1, f1 = input.arrow)""".stripMargin)
|
||||||
"""AbilityA(v1 = 1, f1 = input.arrow)""".stripMargin)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ class InfixTokenSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
|||||||
|
|
||||||
import AquaSpec._
|
import AquaSpec._
|
||||||
|
|
||||||
private def literal(n: Int): ValueToken[Id] = LiteralToken[Id](n.toString, LiteralType.number)
|
private def literal(n: Int): ValueToken[Id] = toNumber(n)
|
||||||
|
|
||||||
private def infixToken(left: ValueToken[Id], right: ValueToken[Id], op: Op) =
|
private def infixToken(left: ValueToken[Id], right: ValueToken[Id], op: Op) =
|
||||||
InfixToken[Id](left, right, op)
|
InfixToken[Id](left, right, op)
|
||||||
|
@ -4,7 +4,19 @@ import aqua.AquaSpec
|
|||||||
import aqua.AquaSpec.{toNumber, toStr, toVar}
|
import aqua.AquaSpec.{toNumber, toStr, toVar}
|
||||||
import aqua.parser.expr.ConstantExpr
|
import aqua.parser.expr.ConstantExpr
|
||||||
import aqua.parser.expr.func.AssignmentExpr
|
import aqua.parser.expr.func.AssignmentExpr
|
||||||
import aqua.parser.lexer.{Ability, CallArrowToken, CollectionToken, IntoArrow, LiteralToken, Name, NamedTypeToken, NamedValueToken, Token, ValueToken, VarToken}
|
import aqua.parser.lexer.{
|
||||||
|
Ability,
|
||||||
|
CallArrowToken,
|
||||||
|
CollectionToken,
|
||||||
|
IntoArrow,
|
||||||
|
LiteralToken,
|
||||||
|
Name,
|
||||||
|
NamedTypeToken,
|
||||||
|
NamedValueToken,
|
||||||
|
Token,
|
||||||
|
ValueToken,
|
||||||
|
VarToken
|
||||||
|
}
|
||||||
import aqua.parser.lexer.CollectionToken.Mode.ArrayMode
|
import aqua.parser.lexer.CollectionToken.Mode.ArrayMode
|
||||||
import aqua.types.LiteralType
|
import aqua.types.LiteralType
|
||||||
import cats.Id
|
import cats.Id
|
||||||
@ -16,9 +28,10 @@ class StructValueExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
|||||||
import AquaSpec._
|
import AquaSpec._
|
||||||
|
|
||||||
private def parseAndCheckStruct(str: String) = {
|
private def parseAndCheckStruct(str: String) = {
|
||||||
val one = LiteralToken[Id]("1", LiteralType.number)
|
|
||||||
val two = LiteralToken[Id]("2", LiteralType.number)
|
val one = toNumber(1)
|
||||||
val three = LiteralToken[Id]("3", LiteralType.number)
|
val two = toNumber(2)
|
||||||
|
val three = toNumber(3)
|
||||||
val a = LiteralToken[Id]("\"a\"", LiteralType.string)
|
val a = LiteralToken[Id]("\"a\"", LiteralType.string)
|
||||||
val b = LiteralToken[Id]("\"b\"", LiteralType.string)
|
val b = LiteralToken[Id]("\"b\"", LiteralType.string)
|
||||||
val c = LiteralToken[Id]("\"c\"", LiteralType.string)
|
val c = LiteralToken[Id]("\"c\"", LiteralType.string)
|
||||||
@ -51,30 +64,39 @@ class StructValueExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
|||||||
|
|
||||||
"one named arg" should "be parsed" in {
|
"one named arg" should "be parsed" in {
|
||||||
val result = aqua.parser.lexer.Token.namedArg
|
val result = aqua.parser.lexer.Token.namedArg
|
||||||
.parseAll(
|
.parseAll(""" a
|
||||||
""" a
|
|
||||||
| =
|
| =
|
||||||
| 3""".stripMargin)
|
| 3""".stripMargin)
|
||||||
.map(v => (v._1, v._2.mapK(spanToId))).value
|
.map(v => (v._1, v._2.mapK(spanToId)))
|
||||||
|
.value
|
||||||
|
|
||||||
result should be(("a", toNumber(3)))
|
result should be(("a", toNumber(3)))
|
||||||
}
|
}
|
||||||
|
|
||||||
"named args" should "be parsed" in {
|
"named args" should "be parsed" in {
|
||||||
val result = Token.namedArgs.parseAll(
|
val result = Token.namedArgs
|
||||||
"""(
|
.parseAll("""(
|
||||||
|a = "str",
|
|a = "str",
|
||||||
|b = 3,
|
|b = 3,
|
||||||
|c
|
|c
|
||||||
| =
|
| =
|
||||||
| 5
|
| 5
|
||||||
|)""".stripMargin).value.map{ case (str, vt) => (str, vt.mapK(spanToId)) }
|
|)""".stripMargin)
|
||||||
|
.value
|
||||||
|
.map { case (str, vt) => (str, vt.mapK(spanToId)) }
|
||||||
|
|
||||||
result should be(NonEmptyList[(String, ValueToken[Id])](("a", toStr("str")), ("b", toNumber(3)) :: ("c", toNumber(5)) :: Nil))
|
result should be(
|
||||||
|
NonEmptyList[(String, ValueToken[Id])](
|
||||||
|
("a", toStr("str")),
|
||||||
|
("b", toNumber(3)) :: ("c", toNumber(5)) :: Nil
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
"one line struct value" should "be parsed" in {
|
"one line struct value" should "be parsed" in {
|
||||||
parseAndCheckStruct("""Obj(f1 = 1, f2 = "a", f3 = [1,2,3], f4=["b", "c"], f5 =NestedObj(i1 = 2, i2 = "b", i3= funcCall(3), i4 = value), f6=funcCall(1), f7 = Serv.call(2))""")
|
parseAndCheckStruct(
|
||||||
|
"""Obj(f1 = 1, f2 = "a", f3 = [1,2,3], f4=["b", "c"], f5 =NestedObj(i1 = 2, i2 = "b", i3= funcCall(3), i4 = value), f6=funcCall(1), f7 = Serv.call(2))"""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
"multiline line struct value" should "be parsed" in {
|
"multiline line struct value" should "be parsed" in {
|
||||||
@ -91,7 +113,8 @@ class StructValueExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
|||||||
| i1
|
| i1
|
||||||
| =
|
| =
|
||||||
| 2,
|
| 2,
|
||||||
| i2 = "b", i3= funcCall(3), i4 = value), f6=funcCall(1), f7 = Serv.call(2))""".stripMargin)
|
| i2 = "b", i3= funcCall(3), i4 = value), f6=funcCall(1), f7 = Serv.call(2))""".stripMargin
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,12 +27,15 @@ class PropertyOpSpec extends AnyFlatSpec with Matchers with EitherValues {
|
|||||||
val idx2 = PropertyOp.ops.parseAll("[ 1 ]").value.map(_.mapK(spanToId)).head
|
val idx2 = PropertyOp.ops.parseAll("[ 1 ]").value.map(_.mapK(spanToId)).head
|
||||||
idx2 shouldBe IntoIndex[Id]((), Option(toNumber(1)))
|
idx2 shouldBe IntoIndex[Id]((), Option(toNumber(1)))
|
||||||
|
|
||||||
val idx3 = PropertyOp.ops.parseAll(
|
val idx3 = PropertyOp.ops
|
||||||
"""[ -- comment1
|
.parseAll("""[ -- comment1
|
||||||
| -- comment2
|
| -- comment2
|
||||||
| 1 -- comment3
|
| 1 -- comment3
|
||||||
| -- comment4
|
| -- comment4
|
||||||
|]""".stripMargin).value.map(_.mapK(spanToId)).head
|
|]""".stripMargin)
|
||||||
|
.value
|
||||||
|
.map(_.mapK(spanToId))
|
||||||
|
.head
|
||||||
idx3 shouldBe IntoIndex[Id]((), Option(toNumber(1)))
|
idx3 shouldBe IntoIndex[Id]((), Option(toNumber(1)))
|
||||||
|
|
||||||
PropertyOp.ops.parseAll("[-1]").isLeft shouldBe true
|
PropertyOp.ops.parseAll("[-1]").isLeft shouldBe true
|
||||||
@ -48,7 +51,7 @@ class PropertyOpSpec extends AnyFlatSpec with Matchers with EitherValues {
|
|||||||
(),
|
(),
|
||||||
NonEmptyMap.of(
|
NonEmptyMap.of(
|
||||||
"a" -> LiteralToken("\"str\"", LiteralType.string),
|
"a" -> LiteralToken("\"str\"", LiteralType.string),
|
||||||
"b" -> LiteralToken("12", LiteralType.number)
|
"b" -> toNumber(12)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -60,13 +63,13 @@ class PropertyOpSpec extends AnyFlatSpec with Matchers with EitherValues {
|
|||||||
(),
|
(),
|
||||||
NonEmptyMap.of(
|
NonEmptyMap.of(
|
||||||
"a" -> LiteralToken("\"str\"", LiteralType.string),
|
"a" -> LiteralToken("\"str\"", LiteralType.string),
|
||||||
"b" -> LiteralToken("12", LiteralType.number)
|
"b" -> toNumber(12)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
IntoCopy[Id](
|
IntoCopy[Id](
|
||||||
(),
|
(),
|
||||||
NonEmptyMap.of(
|
NonEmptyMap.of(
|
||||||
"c" -> LiteralToken("54", LiteralType.number),
|
"c" -> toNumber(54),
|
||||||
"d" -> VarToken("someVar")
|
"d" -> VarToken("someVar")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -12,7 +12,9 @@ class ValueTokenSpec extends AnyFlatSpec with Matchers with EitherValues {
|
|||||||
import aqua.AquaSpec._
|
import aqua.AquaSpec._
|
||||||
|
|
||||||
"var getter" should "parse" in {
|
"var getter" should "parse" in {
|
||||||
ValueToken.`value`.parseAll("varname").value.mapK(spanToId) should be(VarToken(Name[Id]("varname"), Nil))
|
ValueToken.`value`.parseAll("varname").value.mapK(spanToId) should be(
|
||||||
|
VarToken(Name[Id]("varname"), Nil)
|
||||||
|
)
|
||||||
ValueToken.`value`.parseAll("varname.field").value.mapK(spanToId) should be(
|
ValueToken.`value`.parseAll("varname.field").value.mapK(spanToId) should be(
|
||||||
VarToken(Name[Id]("varname"), IntoField[Id]("field") :: Nil)
|
VarToken(Name[Id]("varname"), IntoField[Id]("field") :: Nil)
|
||||||
)
|
)
|
||||||
@ -22,17 +24,40 @@ class ValueTokenSpec extends AnyFlatSpec with Matchers with EitherValues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"literals" should "parse" in {
|
"literals" should "parse" in {
|
||||||
ValueToken.`value`.parseAll("true").value.mapK(spanToId) should be(LiteralToken[Id]("true", LiteralType.bool))
|
ValueToken.`value`.parseAll("true").value.mapK(spanToId) should be(
|
||||||
ValueToken.`value`.parseAll("false").value.mapK(spanToId) should be(LiteralToken[Id]("false", LiteralType.bool))
|
LiteralToken[Id]("true", LiteralType.bool)
|
||||||
|
)
|
||||||
|
ValueToken.`value`.parseAll("false").value.mapK(spanToId) should be(
|
||||||
|
LiteralToken[Id]("false", LiteralType.bool)
|
||||||
|
)
|
||||||
|
|
||||||
ValueToken.`value`.parseAll("1").value.mapK(spanToId) should be(LiteralToken[Id]("1", LiteralType.number))
|
ValueToken.`value`.parseAll("-1").value.mapK(spanToId) should be(
|
||||||
ValueToken.`value`.parseAll("1111").value.mapK(spanToId) should be(LiteralToken[Id]("1111", LiteralType.number))
|
LiteralToken[Id]("-1", LiteralType.signed)
|
||||||
|
)
|
||||||
|
ValueToken.`value`.parseAll("-1111").value.mapK(spanToId) should be(
|
||||||
|
LiteralToken[Id]("-1111", LiteralType.signed)
|
||||||
|
)
|
||||||
|
|
||||||
ValueToken.`value`.parseAll("-1543").value.mapK(spanToId) should be(LiteralToken[Id]("-1543", LiteralType.signed))
|
ValueToken.`value`.parseAll("1").value.mapK(spanToId) should be(
|
||||||
|
LiteralToken[Id]("1", LiteralType.unsigned)
|
||||||
|
)
|
||||||
|
ValueToken.`value`.parseAll("1111").value.mapK(spanToId) should be(
|
||||||
|
LiteralToken[Id]("1111", LiteralType.unsigned)
|
||||||
|
)
|
||||||
|
|
||||||
ValueToken.`value`.parseAll("1.0").value.mapK(spanToId) should be(LiteralToken[Id]("1.0", LiteralType.float))
|
ValueToken.`value`.parseAll("-1543").value.mapK(spanToId) should be(
|
||||||
ValueToken.`value`.parseAll("1.23").value.mapK(spanToId) should be(LiteralToken[Id]("1.23", LiteralType.float))
|
LiteralToken[Id]("-1543", LiteralType.signed)
|
||||||
ValueToken.`value`.parseAll("-1.23").value.mapK(spanToId) should be(LiteralToken[Id]("-1.23", LiteralType.float))
|
)
|
||||||
|
|
||||||
|
ValueToken.`value`.parseAll("1.0").value.mapK(spanToId) should be(
|
||||||
|
LiteralToken[Id]("1.0", LiteralType.float)
|
||||||
|
)
|
||||||
|
ValueToken.`value`.parseAll("1.23").value.mapK(spanToId) should be(
|
||||||
|
LiteralToken[Id]("1.23", LiteralType.float)
|
||||||
|
)
|
||||||
|
ValueToken.`value`.parseAll("-1.23").value.mapK(spanToId) should be(
|
||||||
|
LiteralToken[Id]("-1.23", LiteralType.float)
|
||||||
|
)
|
||||||
|
|
||||||
ValueToken.`value`.parseAll("\"some crazy string\"").value.mapK(spanToId) should be(
|
ValueToken.`value`.parseAll("\"some crazy string\"").value.mapK(spanToId) should be(
|
||||||
LiteralToken[Id]("\"some crazy string\"", LiteralType.string)
|
LiteralToken[Id]("\"some crazy string\"", LiteralType.string)
|
||||||
|
@ -8,10 +8,14 @@ import aqua.semantics.rules.definitions.DefinitionsState
|
|||||||
import aqua.semantics.rules.locations.LocationsState
|
import aqua.semantics.rules.locations.LocationsState
|
||||||
import aqua.semantics.rules.names.NamesState
|
import aqua.semantics.rules.names.NamesState
|
||||||
import aqua.semantics.rules.types.TypesState
|
import aqua.semantics.rules.types.TypesState
|
||||||
|
import aqua.semantics.rules.errors.ReportErrors
|
||||||
|
|
||||||
import cats.Semigroup
|
import cats.Semigroup
|
||||||
import cats.data.{Chain, State}
|
import cats.data.{Chain, State}
|
||||||
import cats.kernel.Monoid
|
import cats.kernel.Monoid
|
||||||
import cats.syntax.monoid.*
|
import cats.syntax.monoid.*
|
||||||
|
import monocle.Lens
|
||||||
|
import monocle.macros.GenLens
|
||||||
|
|
||||||
case class CompilerState[S[_]](
|
case class CompilerState[S[_]](
|
||||||
errors: Chain[SemanticError[S]] = Chain.empty[SemanticError[S]],
|
errors: Chain[SemanticError[S]] = Chain.empty[SemanticError[S]],
|
||||||
@ -32,6 +36,30 @@ object CompilerState {
|
|||||||
types = TypesState.init[F](ctx)
|
types = TypesState.init[F](ctx)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
given [S[_]]: Lens[CompilerState[S], NamesState[S]] =
|
||||||
|
GenLens[CompilerState[S]](_.names)
|
||||||
|
|
||||||
|
given [S[_]]: Lens[CompilerState[S], AbilitiesState[S]] =
|
||||||
|
GenLens[CompilerState[S]](_.abilities)
|
||||||
|
|
||||||
|
given [S[_]]: Lens[CompilerState[S], TypesState[S]] =
|
||||||
|
GenLens[CompilerState[S]](_.types)
|
||||||
|
|
||||||
|
given [S[_]]: Lens[CompilerState[S], DefinitionsState[S]] =
|
||||||
|
GenLens[CompilerState[S]](_.definitions)
|
||||||
|
|
||||||
|
given [S[_]]: ReportErrors[S, CompilerState[S]] =
|
||||||
|
new ReportErrors[S, CompilerState[S]] {
|
||||||
|
import monocle.syntax.all.*
|
||||||
|
|
||||||
|
override def apply(
|
||||||
|
st: CompilerState[S],
|
||||||
|
token: Token[S],
|
||||||
|
hints: List[String]
|
||||||
|
): CompilerState[S] =
|
||||||
|
st.focus(_.errors).modify(_.append(RulesViolated(token, hints)))
|
||||||
|
}
|
||||||
|
|
||||||
implicit def compilerStateMonoid[S[_]]: Monoid[St[S]] = new Monoid[St[S]] {
|
implicit def compilerStateMonoid[S[_]]: Monoid[St[S]] = new Monoid[St[S]] {
|
||||||
override def empty: St[S] = State.pure(Raw.Empty("compiler state monoid empty"))
|
override def empty: St[S] = State.pure(Raw.Empty("compiler state monoid empty"))
|
||||||
|
|
||||||
|
@ -309,26 +309,6 @@ object RawSemantics extends Logging {
|
|||||||
def transpile[S[_]](
|
def transpile[S[_]](
|
||||||
ast: Ast[S]
|
ast: Ast[S]
|
||||||
)(implicit locations: LocationsAlgebra[S, Interpreter[S, *]]): Interpreter[S, Raw] = {
|
)(implicit locations: LocationsAlgebra[S, Interpreter[S, *]]): Interpreter[S, Raw] = {
|
||||||
import monocle.syntax.all.*
|
|
||||||
|
|
||||||
implicit val re: ReportErrors[S, CompilerState[S]] = new ReportErrors[S, CompilerState[S]] {
|
|
||||||
override def apply(
|
|
||||||
st: CompilerState[S],
|
|
||||||
token: Token[S],
|
|
||||||
hints: List[String]
|
|
||||||
): CompilerState[S] =
|
|
||||||
st.focus(_.errors).modify(_.append(RulesViolated(token, hints)))
|
|
||||||
}
|
|
||||||
|
|
||||||
implicit val ns: Lens[CompilerState[S], NamesState[S]] = GenLens[CompilerState[S]](_.names)
|
|
||||||
|
|
||||||
implicit val as: Lens[CompilerState[S], AbilitiesState[S]] =
|
|
||||||
GenLens[CompilerState[S]](_.abilities)
|
|
||||||
|
|
||||||
implicit val ts: Lens[CompilerState[S], TypesState[S]] = GenLens[CompilerState[S]](_.types)
|
|
||||||
|
|
||||||
implicit val ds: Lens[CompilerState[S], DefinitionsState[S]] =
|
|
||||||
GenLens[CompilerState[S]](_.definitions)
|
|
||||||
|
|
||||||
implicit val typesInterpreter: TypesInterpreter[S, CompilerState[S]] =
|
implicit val typesInterpreter: TypesInterpreter[S, CompilerState[S]] =
|
||||||
new TypesInterpreter[S, CompilerState[S]]
|
new TypesInterpreter[S, CompilerState[S]]
|
||||||
|
@ -17,6 +17,7 @@ import cats.syntax.traverse.*
|
|||||||
import cats.syntax.option.*
|
import cats.syntax.option.*
|
||||||
import cats.instances.list.*
|
import cats.instances.list.*
|
||||||
import cats.data.{NonEmptyList, NonEmptyMap}
|
import cats.data.{NonEmptyList, NonEmptyMap}
|
||||||
|
import scribe.Logging
|
||||||
|
|
||||||
import scala.collection.immutable.SortedMap
|
import scala.collection.immutable.SortedMap
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
|||||||
N: NamesAlgebra[S, Alg],
|
N: NamesAlgebra[S, Alg],
|
||||||
T: TypesAlgebra[S, Alg],
|
T: TypesAlgebra[S, Alg],
|
||||||
A: AbilitiesAlgebra[S, Alg]
|
A: AbilitiesAlgebra[S, Alg]
|
||||||
) {
|
) extends Logging {
|
||||||
|
|
||||||
def ensureIsString(v: ValueToken[S]): Alg[Boolean] =
|
def ensureIsString(v: ValueToken[S]): Alg[Boolean] =
|
||||||
ensureTypeMatches(v, LiteralType.string)
|
ensureTypeMatches(v, LiteralType.string)
|
||||||
@ -140,7 +141,6 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
|||||||
Some(AbilityRaw(rf, scope))
|
Some(AbilityRaw(rf, scope))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
.getOrElse(BottomType -> None)
|
.getOrElse(BottomType -> None)
|
||||||
(typeFromFields, data) = typeFromFieldsWithData
|
(typeFromFields, data) = typeFromFieldsWithData
|
||||||
@ -175,50 +175,75 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
|||||||
callArrowToRaw(ca).map(_.widen[ValueRaw])
|
callArrowToRaw(ca).map(_.widen[ValueRaw])
|
||||||
|
|
||||||
case it @ InfixToken(l, r, _) =>
|
case it @ InfixToken(l, r, _) =>
|
||||||
(valueToRaw(l), valueToRaw(r)).mapN((ll, rr) => ll -> rr).flatMap {
|
(valueToRaw(l), valueToRaw(r)).flatMapN {
|
||||||
case (Some(leftRaw), Some(rightRaw)) =>
|
case (Some(leftRaw), Some(rightRaw)) =>
|
||||||
// TODO handle literal types
|
val lType = leftRaw.`type`
|
||||||
val hasFloats = ScalarType.float
|
val rType = rightRaw.`type`
|
||||||
.exists(ft => leftRaw.`type`.acceptsValueOf(ft) || rightRaw.`type`.acceptsValueOf(ft))
|
lazy val uType = lType `∪` rType
|
||||||
|
val hasFloat = List(lType, rType).exists(
|
||||||
// https://github.com/fluencelabs/aqua-lib/blob/main/math.aqua
|
_ acceptsValueOf LiteralType.float
|
||||||
// Expected types of left and right operands, result type if known, service ID and function name
|
|
||||||
val (leftType, rightType, res, id, fn) = it.op match {
|
|
||||||
case Op.Add =>
|
|
||||||
(ScalarType.i64, ScalarType.i64, None, "math", "add")
|
|
||||||
case Op.Sub => (ScalarType.i64, ScalarType.i64, None, "math", "sub")
|
|
||||||
case Op.Mul if hasFloats =>
|
|
||||||
// TODO may it be i32?
|
|
||||||
(ScalarType.f64, ScalarType.f64, Some(ScalarType.i64), "math", "fmul")
|
|
||||||
case Op.Mul =>
|
|
||||||
(ScalarType.i64, ScalarType.i64, None, "math", "mul")
|
|
||||||
case Op.Div => (ScalarType.i64, ScalarType.i64, None, "math", "div")
|
|
||||||
case Op.Rem => (ScalarType.i64, ScalarType.i64, None, "math", "rem")
|
|
||||||
case Op.Pow => (ScalarType.i64, ScalarType.u32, None, "math", "pow")
|
|
||||||
case Op.Gt => (ScalarType.i64, ScalarType.i64, Some(ScalarType.bool), "cmp", "gt")
|
|
||||||
case Op.Gte => (ScalarType.i64, ScalarType.i64, Some(ScalarType.bool), "cmp", "gte")
|
|
||||||
case Op.Lt => (ScalarType.i64, ScalarType.i64, Some(ScalarType.bool), "cmp", "lt")
|
|
||||||
case Op.Lte => (ScalarType.i64, ScalarType.i64, Some(ScalarType.bool), "cmp", "lte")
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
ltm <- T.ensureTypeMatches(l, leftType, leftRaw.`type`)
|
|
||||||
rtm <- T.ensureTypeMatches(r, rightType, rightRaw.`type`)
|
|
||||||
} yield Option.when(ltm && rtm)(
|
|
||||||
CallArrowRaw(
|
|
||||||
Some(id),
|
|
||||||
fn,
|
|
||||||
leftRaw :: rightRaw :: Nil,
|
|
||||||
ArrowType(
|
|
||||||
ProductType(leftType :: rightType :: Nil),
|
|
||||||
ProductType(
|
|
||||||
res.getOrElse(
|
|
||||||
// If result type is not known/enforced, then assume it's the widest type of operands
|
|
||||||
// E.g. 1:i8 + 1:i8 -> i8, not i64
|
|
||||||
leftRaw.`type` `∪` rightRaw.`type`
|
|
||||||
) :: Nil
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// See https://github.com/fluencelabs/aqua-lib/blob/main/math.aqua
|
||||||
|
val (id, fn) = it.op match {
|
||||||
|
case Op.Add => ("math", "add")
|
||||||
|
case Op.Sub => ("math", "sub")
|
||||||
|
case Op.Mul if hasFloat => ("math", "fmul")
|
||||||
|
case Op.Mul => ("math", "mul")
|
||||||
|
case Op.Div => ("math", "div")
|
||||||
|
case Op.Rem => ("math", "rem")
|
||||||
|
case Op.Pow => ("math", "pow")
|
||||||
|
case Op.Gt => ("cmp", "gt")
|
||||||
|
case Op.Gte => ("cmp", "gte")
|
||||||
|
case Op.Lt => ("cmp", "lt")
|
||||||
|
case Op.Lte => ("cmp", "lte")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If `uType == TopType`, it means that we don't
|
||||||
|
* have type big enough to hold the result of operation.
|
||||||
|
* e.g. We will use `i64` for result of `i32 * u64`
|
||||||
|
* TODO: Handle this more gracefully
|
||||||
|
* (use warning system when it is implemented)
|
||||||
|
*/
|
||||||
|
def uTypeBounded = if (uType == TopType) {
|
||||||
|
val bounded = ScalarType.i64
|
||||||
|
logger.warn(
|
||||||
|
s"Result type of ($lType ${it.op} $rType) is $TopType, " +
|
||||||
|
s"using $bounded instead"
|
||||||
|
)
|
||||||
|
bounded
|
||||||
|
} else uType
|
||||||
|
|
||||||
|
// Expected type sets of left and right operands, result type
|
||||||
|
val (leftExp, rightExp, resType) = it.op match {
|
||||||
|
case Op.Add | Op.Sub | Op.Div | Op.Rem =>
|
||||||
|
(ScalarType.integer, ScalarType.integer, uTypeBounded)
|
||||||
|
case Op.Pow =>
|
||||||
|
(ScalarType.integer, ScalarType.unsigned, uTypeBounded)
|
||||||
|
case Op.Mul if hasFloat =>
|
||||||
|
(ScalarType.float, ScalarType.float, ScalarType.i64)
|
||||||
|
case Op.Mul =>
|
||||||
|
(ScalarType.integer, ScalarType.integer, uTypeBounded)
|
||||||
|
case Op.Gt | Op.Lt | Op.Gte | Op.Lte =>
|
||||||
|
(ScalarType.integer, ScalarType.integer, ScalarType.bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
leftChecked <- T.ensureTypeOneOf(l, leftExp, lType)
|
||||||
|
rightChecked <- T.ensureTypeOneOf(r, rightExp, rType)
|
||||||
|
} yield Option.when(
|
||||||
|
leftChecked.isDefined && rightChecked.isDefined
|
||||||
|
)(
|
||||||
|
CallArrowRaw(
|
||||||
|
ability = Some(id),
|
||||||
|
name = fn,
|
||||||
|
arguments = leftRaw :: rightRaw :: Nil,
|
||||||
|
baseType = ArrowType(
|
||||||
|
ProductType(lType :: rType :: Nil),
|
||||||
|
ProductType(resType :: Nil)
|
||||||
),
|
),
|
||||||
Some(LiteralRaw.quote(id))
|
serviceId = Some(LiteralRaw.quote(id))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -228,9 +253,14 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate CallArrowRaw for arrow in ability
|
// Generate CallArrowRaw for arrow in ability
|
||||||
def callAbType(ab: String, abType: AbilityType, ca: CallArrowToken[S]): Alg[Option[CallArrowRaw]] =
|
def callAbType(
|
||||||
|
ab: String,
|
||||||
|
abType: AbilityType,
|
||||||
|
ca: CallArrowToken[S]
|
||||||
|
): Alg[Option[CallArrowRaw]] =
|
||||||
abType.arrows.get(ca.funcName.value) match {
|
abType.arrows.get(ca.funcName.value) match {
|
||||||
case Some(arrowType) => Option(CallArrowRaw(None, s"$ab.${ca.funcName.value}", Nil, arrowType, None)).pure[Alg]
|
case Some(arrowType) =>
|
||||||
|
Option(CallArrowRaw(None, s"$ab.${ca.funcName.value}", Nil, arrowType, None)).pure[Alg]
|
||||||
case None => None.pure[Alg]
|
case None => None.pure[Alg]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +309,6 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
|||||||
case _ => none
|
case _ => none
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
result <- raw.flatTraverse(r =>
|
result <- raw.flatTraverse(r =>
|
||||||
val arr = r.baseType
|
val arr = r.baseType
|
||||||
|
@ -36,6 +36,12 @@ trait TypesAlgebra[S[_], Alg[_]] {
|
|||||||
|
|
||||||
def ensureTypeMatches(token: Token[S], expected: Type, givenType: Type): Alg[Boolean]
|
def ensureTypeMatches(token: Token[S], expected: Type, givenType: Type): Alg[Boolean]
|
||||||
|
|
||||||
|
def ensureTypeOneOf[T <: Type](
|
||||||
|
token: Token[S],
|
||||||
|
expected: Set[T],
|
||||||
|
givenType: Type
|
||||||
|
): Alg[Option[Type]]
|
||||||
|
|
||||||
def expectNoExport(token: Token[S]): Alg[Unit]
|
def expectNoExport(token: Token[S]): Alg[Unit]
|
||||||
|
|
||||||
def checkArgumentsNumber(token: Token[S], expected: Int, givenNum: Int): Alg[Boolean]
|
def checkArgumentsNumber(token: Token[S], expected: Int, givenNum: Int): Alg[Boolean]
|
||||||
|
@ -36,6 +36,7 @@ import cats.syntax.flatMap.*
|
|||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
import cats.{~>, Applicative}
|
import cats.{~>, Applicative}
|
||||||
|
import cats.syntax.option.*
|
||||||
import monocle.Lens
|
import monocle.Lens
|
||||||
import monocle.macros.GenLens
|
import monocle.macros.GenLens
|
||||||
|
|
||||||
@ -296,6 +297,21 @@ class TypesInterpreter[S[_], X](implicit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def ensureTypeOneOf[T <: Type](
|
||||||
|
token: Token[S],
|
||||||
|
expected: Set[T],
|
||||||
|
givenType: Type
|
||||||
|
): State[X, Option[Type]] = expected
|
||||||
|
.find(_ acceptsValueOf givenType)
|
||||||
|
.fold(
|
||||||
|
reportError(
|
||||||
|
token,
|
||||||
|
"Types mismatch." ::
|
||||||
|
s"expected one of: ${expected.mkString(", ")}" ::
|
||||||
|
s"given: $givenType" :: Nil
|
||||||
|
).as(none)
|
||||||
|
)(_.some.pure)
|
||||||
|
|
||||||
override def expectNoExport(token: Token[S]): State[X, Unit] =
|
override def expectNoExport(token: Token[S]): State[X, Unit] =
|
||||||
report(
|
report(
|
||||||
token,
|
token,
|
||||||
|
206
semantics/src/test/scala/aqua/semantics/ValuesAlgebraSpec.scala
Normal file
206
semantics/src/test/scala/aqua/semantics/ValuesAlgebraSpec.scala
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
package aqua.semantics
|
||||||
|
|
||||||
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
|
import aqua.semantics.rules.names.NamesState
|
||||||
|
import aqua.semantics.rules.abilities.AbilitiesState
|
||||||
|
import aqua.semantics.rules.types.TypesState
|
||||||
|
import aqua.semantics.rules.types.TypesAlgebra
|
||||||
|
import aqua.semantics.rules.abilities.AbilitiesInterpreter
|
||||||
|
import aqua.semantics.rules.names.NamesAlgebra
|
||||||
|
import aqua.semantics.rules.definitions.DefinitionsAlgebra
|
||||||
|
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||||
|
import aqua.semantics.rules.names.NamesInterpreter
|
||||||
|
import aqua.semantics.rules.definitions.DefinitionsInterpreter
|
||||||
|
import aqua.semantics.rules.types.TypesInterpreter
|
||||||
|
import aqua.semantics.rules.locations.LocationsAlgebra
|
||||||
|
import aqua.semantics.rules.locations.DummyLocationsInterpreter
|
||||||
|
import aqua.raw.value.LiteralRaw
|
||||||
|
import aqua.raw.RawContext
|
||||||
|
import aqua.types.{LiteralType, ScalarType, TopType, Type}
|
||||||
|
import aqua.parser.lexer.{InfixToken, LiteralToken, Name, ValueToken, VarToken}
|
||||||
|
|
||||||
|
import org.scalatest.flatspec.AnyFlatSpec
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.Inside
|
||||||
|
import cats.Id
|
||||||
|
import cats.data.State
|
||||||
|
import cats.syntax.functor.*
|
||||||
|
import cats.syntax.comonad.*
|
||||||
|
import monocle.syntax.all.*
|
||||||
|
|
||||||
|
class ValuesAlgebraSpec extends AnyFlatSpec with Matchers with Inside {
|
||||||
|
|
||||||
|
type TestState = CompilerState[Id]
|
||||||
|
|
||||||
|
def algebra() = {
|
||||||
|
type Interpreter[A] = State[TestState, A]
|
||||||
|
|
||||||
|
given LocationsAlgebra[Id, Interpreter] =
|
||||||
|
new DummyLocationsInterpreter[Id, CompilerState[Id]]
|
||||||
|
|
||||||
|
given TypesAlgebra[Id, Interpreter] =
|
||||||
|
new TypesInterpreter[Id, CompilerState[Id]]
|
||||||
|
given AbilitiesAlgebra[Id, Interpreter] =
|
||||||
|
new AbilitiesInterpreter[Id, CompilerState[Id]]
|
||||||
|
given NamesAlgebra[Id, Interpreter] =
|
||||||
|
new NamesInterpreter[Id, CompilerState[Id]]
|
||||||
|
given DefinitionsAlgebra[Id, Interpreter] =
|
||||||
|
new DefinitionsInterpreter[Id, CompilerState[Id]]
|
||||||
|
|
||||||
|
new ValuesAlgebra[Id, Interpreter]
|
||||||
|
}
|
||||||
|
|
||||||
|
def literal(value: String, `type`: LiteralType) =
|
||||||
|
LiteralToken(Id(value), `type`)
|
||||||
|
|
||||||
|
def variable(name: String) =
|
||||||
|
VarToken(Name(Id(name)), Nil)
|
||||||
|
|
||||||
|
def allPairs[A](list: List[A]): List[(A, A)] = for {
|
||||||
|
a <- list
|
||||||
|
b <- list
|
||||||
|
} yield (a, b)
|
||||||
|
|
||||||
|
def genState(vars: Map[String, Type] = Map.empty) =
|
||||||
|
CompilerState
|
||||||
|
.init[Id](RawContext.blank)
|
||||||
|
.focus(_.names)
|
||||||
|
.modify(
|
||||||
|
_.focus(_.stack).modify(
|
||||||
|
NamesState.Frame(
|
||||||
|
token = Name(Id("test")), // Token just for test
|
||||||
|
names = vars
|
||||||
|
) :: _
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
"valueToRaw" should "handle +, -, /, *, % on number literals" in {
|
||||||
|
val types = List(
|
||||||
|
LiteralType.signed,
|
||||||
|
LiteralType.unsigned
|
||||||
|
)
|
||||||
|
|
||||||
|
allPairs(types).foreach { case (lt, rt) =>
|
||||||
|
val llit = literal("42", lt)
|
||||||
|
val rlit = literal("37", rt)
|
||||||
|
|
||||||
|
val alg = algebra()
|
||||||
|
|
||||||
|
InfixToken.Op.math
|
||||||
|
.filterNot(
|
||||||
|
// Can not use negative numbers with pow
|
||||||
|
_ == InfixToken.Op.Pow && rt != LiteralType.unsigned
|
||||||
|
)
|
||||||
|
.foreach { op =>
|
||||||
|
val token = InfixToken[Id](llit, rlit, op)
|
||||||
|
|
||||||
|
val (st, res) = alg
|
||||||
|
.valueToRaw(token)
|
||||||
|
.run(genState())
|
||||||
|
.value
|
||||||
|
|
||||||
|
val t = if (lt == rt) lt else LiteralType.signed
|
||||||
|
|
||||||
|
inside(res) { case Some(value) =>
|
||||||
|
value.`type` shouldBe t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "handle +, -, /, *, % on number vars" in {
|
||||||
|
allPairs(ScalarType.integer.toList).foreach { case (lt, rt) =>
|
||||||
|
val vl = variable("left")
|
||||||
|
val vr = variable("right")
|
||||||
|
|
||||||
|
val ut = lt.uniteTop(rt)
|
||||||
|
|
||||||
|
val state = genState(
|
||||||
|
vars = Map(
|
||||||
|
"left" -> lt,
|
||||||
|
"right" -> rt
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val alg = algebra()
|
||||||
|
|
||||||
|
InfixToken.Op.math
|
||||||
|
.filterNot(
|
||||||
|
// Can not use negative numbers with pow
|
||||||
|
_ == InfixToken.Op.Pow && ScalarType.signed(rt)
|
||||||
|
)
|
||||||
|
.foreach { op =>
|
||||||
|
val token = InfixToken[Id](vl, vr, op)
|
||||||
|
|
||||||
|
val (st, res) = alg
|
||||||
|
.valueToRaw(token)
|
||||||
|
.run(state)
|
||||||
|
.value
|
||||||
|
|
||||||
|
inside(res) { case Some(value) =>
|
||||||
|
value.`type` shouldBe a[ScalarType]
|
||||||
|
|
||||||
|
if (ut != TopType) {
|
||||||
|
value.`type`.acceptsValueOf(lt) shouldBe true
|
||||||
|
value.`type`.acceptsValueOf(rt) shouldBe true
|
||||||
|
} else {
|
||||||
|
// This should happen only if
|
||||||
|
// of the types is 64 bit
|
||||||
|
List(lt, rt).exists(
|
||||||
|
List(ScalarType.u64, ScalarType.i64).contains
|
||||||
|
) shouldBe true
|
||||||
|
|
||||||
|
(value.`type`.acceptsValueOf(lt) ||
|
||||||
|
value.`type`.acceptsValueOf(rt)) shouldBe true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "handle * on float literals" in {
|
||||||
|
val llit = literal("42.1", LiteralType.float)
|
||||||
|
val rlit = literal("37.2", LiteralType.float)
|
||||||
|
|
||||||
|
val alg = algebra()
|
||||||
|
|
||||||
|
val token = InfixToken[Id](llit, rlit, InfixToken.Op.Mul)
|
||||||
|
|
||||||
|
val (st, res) = alg
|
||||||
|
.valueToRaw(token)
|
||||||
|
.run(genState())
|
||||||
|
.value
|
||||||
|
|
||||||
|
inside(res) { case Some(value) =>
|
||||||
|
value.`type` shouldBe ScalarType.i64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "handle * on float vars" in {
|
||||||
|
allPairs(ScalarType.float.toList).foreach { case (lt, rt) =>
|
||||||
|
val lvar = variable("left")
|
||||||
|
val rvar = variable("right")
|
||||||
|
|
||||||
|
val alg = algebra()
|
||||||
|
|
||||||
|
val state = genState(
|
||||||
|
vars = Map(
|
||||||
|
"left" -> lt,
|
||||||
|
"right" -> rt
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val token = InfixToken[Id](lvar, rvar, InfixToken.Op.Mul)
|
||||||
|
|
||||||
|
val (st, res) = alg
|
||||||
|
.valueToRaw(token)
|
||||||
|
.run(state)
|
||||||
|
.value
|
||||||
|
|
||||||
|
inside(res) { case Some(value) =>
|
||||||
|
value.`type` shouldBe ScalarType.i64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -106,19 +106,12 @@ object CompareTypes {
|
|||||||
* -1 if left is a subtype of the right
|
* -1 if left is a subtype of the right
|
||||||
*/
|
*/
|
||||||
def apply(l: Type, r: Type): Double =
|
def apply(l: Type, r: Type): Double =
|
||||||
if (l == r) 0.0
|
|
||||||
else
|
|
||||||
(l, r) match {
|
(l, r) match {
|
||||||
|
case _ if l == r => 0.0
|
||||||
|
|
||||||
case (TopType, _) | (_, BottomType) => 1.0
|
case (TopType, _) | (_, BottomType) => 1.0
|
||||||
case (BottomType, _) | (_, TopType) => -1.0
|
case (BottomType, _) | (_, TopType) => -1.0
|
||||||
|
|
||||||
// Literals and scalars
|
|
||||||
case (x: ScalarType, y: ScalarType) => scalarOrder.partialCompare(x, y)
|
|
||||||
case (LiteralType(xs, _), y: ScalarType) if xs == Set(y) => 0.0
|
|
||||||
case (LiteralType(xs, _), y: ScalarType) if xs(y) => -1.0
|
|
||||||
case (x: ScalarType, LiteralType(ys, _)) if ys == Set(x) => 0.0
|
|
||||||
case (x: ScalarType, LiteralType(ys, _)) if ys(x) => 1.0
|
|
||||||
|
|
||||||
// Collections
|
// Collections
|
||||||
case (x: ArrayType, y: ArrayType) => apply(x.element, y.element)
|
case (x: ArrayType, y: ArrayType) => apply(x.element, y.element)
|
||||||
case (x: ArrayType, y: StreamType) => apply(x.element, y.element)
|
case (x: ArrayType, y: StreamType) => apply(x.element, y.element)
|
||||||
@ -130,6 +123,16 @@ object CompareTypes {
|
|||||||
case (lnt: AbilityType, rnt: AbilityType) => compareNamed(lnt.fields, rnt.fields)
|
case (lnt: AbilityType, rnt: AbilityType) => compareNamed(lnt.fields, rnt.fields)
|
||||||
case (lnt: StructType, rnt: StructType) => compareNamed(lnt.fields, rnt.fields)
|
case (lnt: StructType, rnt: StructType) => compareNamed(lnt.fields, rnt.fields)
|
||||||
|
|
||||||
|
// Literals and scalars
|
||||||
|
case (x: ScalarType, y: ScalarType) => scalarOrder.partialCompare(x, y)
|
||||||
|
case (LiteralType(xs, _), y: ScalarType) if xs == Set(y) => 0.0
|
||||||
|
case (LiteralType(xs, _), y: ScalarType) if xs(y) => -1.0
|
||||||
|
case (x: ScalarType, LiteralType(ys, _)) if ys == Set(x) => 0.0
|
||||||
|
case (x: ScalarType, LiteralType(ys, _)) if ys(x) => 1.0
|
||||||
|
case (LiteralType(xs, _), LiteralType(ys, _)) if xs == ys => 0.0
|
||||||
|
case (LiteralType(xs, _), LiteralType(ys, _)) if xs subsetOf ys => 1.0
|
||||||
|
case (LiteralType(xs, _), LiteralType(ys, _)) if ys subsetOf xs => -1.0
|
||||||
|
|
||||||
// Products
|
// Products
|
||||||
case (l: ProductType, r: ProductType) => compareProducts(l, r)
|
case (l: ProductType, r: ProductType) => compareProducts(l, r)
|
||||||
|
|
||||||
|
@ -172,22 +172,30 @@ object ScalarType {
|
|||||||
val string = ScalarType("string")
|
val string = ScalarType("string")
|
||||||
|
|
||||||
val float = Set(f32, f64)
|
val float = Set(f32, f64)
|
||||||
val signed = float ++ Set(i8, i16, i32, i64)
|
val signed = Set(i8, i16, i32, i64)
|
||||||
val unsigned = Set(u8, u16, u32, u64)
|
val unsigned = Set(u8, u16, u32, u64)
|
||||||
val number = signed ++ unsigned
|
val integer = signed ++ unsigned
|
||||||
|
val number = float ++ integer
|
||||||
val all = number ++ Set(bool, string)
|
val all = number ++ Set(bool, string)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class LiteralType private (oneOf: Set[ScalarType], name: String) extends DataType {
|
case class LiteralType private (oneOf: Set[ScalarType], name: String) extends DataType {
|
||||||
override def toString: String = s"$name:lt"
|
override def toString: String = s"$name literal"
|
||||||
}
|
}
|
||||||
|
|
||||||
object LiteralType {
|
object LiteralType {
|
||||||
val float = LiteralType(ScalarType.float, "float")
|
val float = LiteralType(ScalarType.float, "float")
|
||||||
val signed = LiteralType(ScalarType.signed, "signed")
|
val signed = LiteralType(ScalarType.signed, "signed")
|
||||||
|
/*
|
||||||
|
* Literals without sign could be either signed or unsigned
|
||||||
|
* so `ScalarType.integer` is used here
|
||||||
|
*/
|
||||||
|
val unsigned = LiteralType(ScalarType.integer, "unsigned")
|
||||||
val number = LiteralType(ScalarType.number, "number")
|
val number = LiteralType(ScalarType.number, "number")
|
||||||
val bool = LiteralType(Set(ScalarType.bool), "bool")
|
val bool = LiteralType(Set(ScalarType.bool), "bool")
|
||||||
val string = LiteralType(Set(ScalarType.string), "string")
|
val string = LiteralType(Set(ScalarType.string), "string")
|
||||||
|
|
||||||
|
def forInt(n: Int): LiteralType = if (n < 0) signed else unsigned
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait BoxType extends DataType {
|
sealed trait BoxType extends DataType {
|
||||||
@ -234,7 +242,8 @@ sealed trait NamedType extends Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Struct is an unordered collection of labelled types
|
// Struct is an unordered collection of labelled types
|
||||||
case class StructType(name: String, fields: NonEmptyMap[String, Type]) extends DataType with NamedType {
|
case class StructType(name: String, fields: NonEmptyMap[String, Type])
|
||||||
|
extends DataType with NamedType {
|
||||||
|
|
||||||
override def toString: String =
|
override def toString: String =
|
||||||
s"$name{${fields.map(_.toString).toNel.toList.map(kv => kv._1 + ": " + kv._2).mkString(", ")}}"
|
s"$name{${fields.map(_.toString).toNel.toList.map(kv => kv._1 + ": " + kv._2).mkString(", ")}}"
|
||||||
|
@ -30,8 +30,6 @@ case class UniteTypes(scalarsCombine: ScalarsCombine.T) extends Monoid[Type]:
|
|||||||
|
|
||||||
override def combine(a: Type, b: Type): Type =
|
override def combine(a: Type, b: Type): Type =
|
||||||
(a, b) match {
|
(a, b) match {
|
||||||
case _ if CompareTypes(a, b) == 0.0 => a
|
|
||||||
|
|
||||||
case (ap: ProductType, bp: ProductType) =>
|
case (ap: ProductType, bp: ProductType) =>
|
||||||
combineProducts(ap, bp)
|
combineProducts(ap, bp)
|
||||||
|
|
||||||
@ -75,8 +73,7 @@ case class UniteTypes(scalarsCombine: ScalarsCombine.T) extends Monoid[Type]:
|
|||||||
case 1.0 => a
|
case 1.0 => a
|
||||||
case -1.0 => b
|
case -1.0 => b
|
||||||
case 0.0 => a
|
case 0.0 => a
|
||||||
case _ =>
|
case _ => TopType
|
||||||
TopType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user