mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 14:40:17 +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")
|
||||
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:
|
||||
res = 1 + 2 - 3 * 5 - 2 * 3 / 2 + 5
|
||||
@ -5,4 +8,72 @@ func test1() -> u64:
|
||||
|
||||
func test2() -> u64:
|
||||
res = 2 ** 2 ** (2 * 2 - 2) + 2 - 3 * 5 - 2 * 3 / 2 + 5 + (4 % 2 - 2)
|
||||
<- 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';
|
||||
import {funcsCall} from '../examples/funcsCall.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 {config, isEphemeral} from '../config.js';
|
||||
import {bugLng79Call} from "../examples/canonCall.js";
|
||||
@ -275,6 +275,30 @@ describe('Testing examples', () => {
|
||||
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 () => {
|
||||
let multiReturnResult = await multiReturnCall();
|
||||
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> {
|
||||
return await test1();
|
||||
@ -6,4 +6,20 @@ export async function mathTest1Call(): Promise<number> {
|
||||
|
||||
export async function mathTest2Call(): Promise<number> {
|
||||
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 {
|
||||
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 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)))
|
||||
}
|
||||
|
||||
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 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] = {
|
||||
fieldsAndArrows.toSortedMap.values.flatMap(_.varNames).toSet
|
||||
|
@ -70,7 +70,7 @@ object LiteralModel {
|
||||
|
||||
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 {
|
||||
|
@ -80,10 +80,7 @@ object ModelBuilder {
|
||||
ValueModel.fromRaw(bc.errorHandlingSrvId),
|
||||
bc.errorFuncName,
|
||||
CallRes(
|
||||
ValueModel.lastError :: LiteralModel(
|
||||
i.toString,
|
||||
LiteralType.number
|
||||
) :: Nil,
|
||||
ValueModel.lastError :: LiteralModel.number(i) :: Nil,
|
||||
None
|
||||
),
|
||||
on
|
||||
|
@ -34,7 +34,7 @@ case class LiteralToken[F[_]: Comonad](valueToken: F[String], ts: LiteralType)
|
||||
|
||||
def value: String = valueToken.extract
|
||||
|
||||
override def toString: String = s"$value"
|
||||
override def toString: String = s"$value:$ts"
|
||||
}
|
||||
|
||||
case class CollectionToken[F[_]: Comonad](
|
||||
@ -96,7 +96,8 @@ object CallArrowToken {
|
||||
Name.p
|
||||
~ abilities().? ~ comma0(ValueToken.`value`.surroundedBy(`/s*`))
|
||||
.between(` `.?.with1 *> `(` <* `/s*`, `/s*` *> `)`)
|
||||
).map { case ((n, ab), args) =>
|
||||
)
|
||||
.map { case ((n, ab), args) =>
|
||||
CallBraces(n, ab.map(_.toList).getOrElse(Nil), args)
|
||||
}
|
||||
.withContext(
|
||||
@ -171,6 +172,11 @@ object InfixToken {
|
||||
|
||||
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)] =
|
||||
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 =>
|
||||
fu.extract match {
|
||||
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.Token.LiftToken
|
||||
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 cats.{~>, Id}
|
||||
import org.scalatest.EitherValues
|
||||
@ -60,7 +60,9 @@ object AquaSpec {
|
||||
implicit def toVarIndex(name: String, idx: Int): VarToken[Id] =
|
||||
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 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 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(
|
||||
AbilityIdExpr[Id](toNamedType("Ab"), LiteralToken[Id]("1", LiteralType.number))
|
||||
AbilityIdExpr[Id](toNamedType("Ab"), toNumber(1))
|
||||
)
|
||||
|
||||
parseAbId("Ab a.id") should be(
|
||||
|
@ -16,15 +16,13 @@ class AbilityValueExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
import AquaSpec.*
|
||||
|
||||
private def parseAndCheckAbility(str: String) = {
|
||||
val one = LiteralToken[Id]("1", LiteralType.number)
|
||||
|
||||
parseData(
|
||||
str
|
||||
) should be(
|
||||
NamedValueToken(
|
||||
NamedTypeToken[Id]("AbilityA"),
|
||||
NonEmptyMap.of(
|
||||
"v1" -> one,
|
||||
"v1" -> toNumber(1),
|
||||
"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 {
|
||||
parseAndCheckAbility(
|
||||
"""AbilityA(v1 = 1, f1 = input.arrow)""".stripMargin)
|
||||
parseAndCheckAbility("""AbilityA(v1 = 1, f1 = input.arrow)""".stripMargin)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class InfixTokenSpec extends AnyFlatSpec with Matchers with 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) =
|
||||
InfixToken[Id](left, right, op)
|
||||
|
@ -4,7 +4,19 @@ import aqua.AquaSpec
|
||||
import aqua.AquaSpec.{toNumber, toStr, toVar}
|
||||
import aqua.parser.expr.ConstantExpr
|
||||
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.types.LiteralType
|
||||
import cats.Id
|
||||
@ -16,9 +28,10 @@ class StructValueExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
import AquaSpec._
|
||||
|
||||
private def parseAndCheckStruct(str: String) = {
|
||||
val one = LiteralToken[Id]("1", LiteralType.number)
|
||||
val two = LiteralToken[Id]("2", LiteralType.number)
|
||||
val three = LiteralToken[Id]("3", LiteralType.number)
|
||||
|
||||
val one = toNumber(1)
|
||||
val two = toNumber(2)
|
||||
val three = toNumber(3)
|
||||
val a = LiteralToken[Id]("\"a\"", LiteralType.string)
|
||||
val b = LiteralToken[Id]("\"b\"", 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 {
|
||||
val result = aqua.parser.lexer.Token.namedArg
|
||||
.parseAll(
|
||||
""" a
|
||||
| =
|
||||
| 3""".stripMargin)
|
||||
.map(v => (v._1, v._2.mapK(spanToId))).value
|
||||
.parseAll(""" a
|
||||
| =
|
||||
| 3""".stripMargin)
|
||||
.map(v => (v._1, v._2.mapK(spanToId)))
|
||||
.value
|
||||
|
||||
result should be(("a", toNumber(3)))
|
||||
}
|
||||
|
||||
"named args" should "be parsed" in {
|
||||
val result = Token.namedArgs.parseAll(
|
||||
"""(
|
||||
|a = "str",
|
||||
|b = 3,
|
||||
|c
|
||||
| =
|
||||
| 5
|
||||
|)""".stripMargin).value.map{ case (str, vt) => (str, vt.mapK(spanToId)) }
|
||||
val result = Token.namedArgs
|
||||
.parseAll("""(
|
||||
|a = "str",
|
||||
|b = 3,
|
||||
|c
|
||||
| =
|
||||
| 5
|
||||
|)""".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 {
|
||||
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 {
|
||||
@ -91,7 +113,8 @@ class StructValueExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
| i1
|
||||
| =
|
||||
| 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
|
||||
idx2 shouldBe IntoIndex[Id]((), Option(toNumber(1)))
|
||||
|
||||
val idx3 = PropertyOp.ops.parseAll(
|
||||
"""[ -- comment1
|
||||
| -- comment2
|
||||
| 1 -- comment3
|
||||
| -- comment4
|
||||
|]""".stripMargin).value.map(_.mapK(spanToId)).head
|
||||
val idx3 = PropertyOp.ops
|
||||
.parseAll("""[ -- comment1
|
||||
| -- comment2
|
||||
| 1 -- comment3
|
||||
| -- comment4
|
||||
|]""".stripMargin)
|
||||
.value
|
||||
.map(_.mapK(spanToId))
|
||||
.head
|
||||
idx3 shouldBe IntoIndex[Id]((), Option(toNumber(1)))
|
||||
|
||||
PropertyOp.ops.parseAll("[-1]").isLeft shouldBe true
|
||||
@ -48,7 +51,7 @@ class PropertyOpSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
(),
|
||||
NonEmptyMap.of(
|
||||
"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(
|
||||
"a" -> LiteralToken("\"str\"", LiteralType.string),
|
||||
"b" -> LiteralToken("12", LiteralType.number)
|
||||
"b" -> toNumber(12)
|
||||
)
|
||||
),
|
||||
IntoCopy[Id](
|
||||
(),
|
||||
NonEmptyMap.of(
|
||||
"c" -> LiteralToken("54", LiteralType.number),
|
||||
"c" -> toNumber(54),
|
||||
"d" -> VarToken("someVar")
|
||||
)
|
||||
)
|
||||
|
@ -12,7 +12,9 @@ class ValueTokenSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
import aqua.AquaSpec._
|
||||
|
||||
"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(
|
||||
VarToken(Name[Id]("varname"), IntoField[Id]("field") :: Nil)
|
||||
)
|
||||
@ -22,17 +24,40 @@ class ValueTokenSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
}
|
||||
|
||||
"literals" should "parse" in {
|
||||
ValueToken.`value`.parseAll("true").value.mapK(spanToId) should be(LiteralToken[Id]("true", LiteralType.bool))
|
||||
ValueToken.`value`.parseAll("false").value.mapK(spanToId) should be(LiteralToken[Id]("false", LiteralType.bool))
|
||||
ValueToken.`value`.parseAll("true").value.mapK(spanToId) should be(
|
||||
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("1111").value.mapK(spanToId) should be(LiteralToken[Id]("1111", LiteralType.number))
|
||||
ValueToken.`value`.parseAll("-1").value.mapK(spanToId) should be(
|
||||
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("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("-1543").value.mapK(spanToId) should be(
|
||||
LiteralToken[Id]("-1543", LiteralType.signed)
|
||||
)
|
||||
|
||||
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(
|
||||
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.names.NamesState
|
||||
import aqua.semantics.rules.types.TypesState
|
||||
import aqua.semantics.rules.errors.ReportErrors
|
||||
|
||||
import cats.Semigroup
|
||||
import cats.data.{Chain, State}
|
||||
import cats.kernel.Monoid
|
||||
import cats.syntax.monoid.*
|
||||
import monocle.Lens
|
||||
import monocle.macros.GenLens
|
||||
|
||||
case class CompilerState[S[_]](
|
||||
errors: Chain[SemanticError[S]] = Chain.empty[SemanticError[S]],
|
||||
@ -32,6 +36,30 @@ object CompilerState {
|
||||
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]] {
|
||||
override def empty: St[S] = State.pure(Raw.Empty("compiler state monoid empty"))
|
||||
|
||||
|
@ -309,26 +309,6 @@ object RawSemantics extends Logging {
|
||||
def transpile[S[_]](
|
||||
ast: Ast[S]
|
||||
)(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]] =
|
||||
new TypesInterpreter[S, CompilerState[S]]
|
||||
|
@ -17,6 +17,7 @@ import cats.syntax.traverse.*
|
||||
import cats.syntax.option.*
|
||||
import cats.instances.list.*
|
||||
import cats.data.{NonEmptyList, NonEmptyMap}
|
||||
import scribe.Logging
|
||||
|
||||
import scala.collection.immutable.SortedMap
|
||||
|
||||
@ -24,7 +25,7 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
||||
N: NamesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg],
|
||||
A: AbilitiesAlgebra[S, Alg]
|
||||
) {
|
||||
) extends Logging {
|
||||
|
||||
def ensureIsString(v: ValueToken[S]): Alg[Boolean] =
|
||||
ensureTypeMatches(v, LiteralType.string)
|
||||
@ -129,18 +130,17 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
||||
typeFromFieldsWithData = rawFields
|
||||
.map(rf =>
|
||||
resolvedType match {
|
||||
case struct@StructType(_, _) =>
|
||||
case struct @ StructType(_, _) =>
|
||||
(
|
||||
StructType(typeName.value, rf.map(_.`type`)),
|
||||
Some(MakeStructRaw(rf, struct))
|
||||
)
|
||||
case scope@AbilityType(_, _) =>
|
||||
case scope @ AbilityType(_, _) =>
|
||||
(
|
||||
AbilityType(typeName.value, rf.map(_.`type`)),
|
||||
Some(AbilityRaw(rf, scope))
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
.getOrElse(BottomType -> None)
|
||||
(typeFromFields, data) = typeFromFieldsWithData
|
||||
@ -175,50 +175,75 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
||||
callArrowToRaw(ca).map(_.widen[ValueRaw])
|
||||
|
||||
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)) =>
|
||||
// TODO handle literal types
|
||||
val hasFloats = ScalarType.float
|
||||
.exists(ft => leftRaw.`type`.acceptsValueOf(ft) || rightRaw.`type`.acceptsValueOf(ft))
|
||||
val lType = leftRaw.`type`
|
||||
val rType = rightRaw.`type`
|
||||
lazy val uType = lType `∪` rType
|
||||
val hasFloat = List(lType, rType).exists(
|
||||
_ acceptsValueOf LiteralType.float
|
||||
)
|
||||
|
||||
// https://github.com/fluencelabs/aqua-lib/blob/main/math.aqua
|
||||
// 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")
|
||||
// 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 {
|
||||
ltm <- T.ensureTypeMatches(l, leftType, leftRaw.`type`)
|
||||
rtm <- T.ensureTypeMatches(r, rightType, rightRaw.`type`)
|
||||
} yield Option.when(ltm && rtm)(
|
||||
leftChecked <- T.ensureTypeOneOf(l, leftExp, lType)
|
||||
rightChecked <- T.ensureTypeOneOf(r, rightExp, rType)
|
||||
} yield Option.when(
|
||||
leftChecked.isDefined && rightChecked.isDefined
|
||||
)(
|
||||
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
|
||||
)
|
||||
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
|
||||
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 {
|
||||
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]
|
||||
}
|
||||
|
||||
@ -279,7 +309,6 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
||||
case _ => none
|
||||
}
|
||||
}
|
||||
|
||||
)
|
||||
result <- raw.flatTraverse(r =>
|
||||
val arr = r.baseType
|
||||
|
@ -36,6 +36,12 @@ trait TypesAlgebra[S[_], Alg[_]] {
|
||||
|
||||
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 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.traverse.*
|
||||
import cats.{~>, Applicative}
|
||||
import cats.syntax.option.*
|
||||
import monocle.Lens
|
||||
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] =
|
||||
report(
|
||||
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,46 +106,49 @@ object CompareTypes {
|
||||
* -1 if left is a subtype of the right
|
||||
*/
|
||||
def apply(l: Type, r: Type): Double =
|
||||
if (l == r) 0.0
|
||||
else
|
||||
(l, r) match {
|
||||
case (TopType, _) | (_, BottomType) => 1.0
|
||||
case (BottomType, _) | (_, TopType) => -1.0
|
||||
(l, r) match {
|
||||
case _ if l == r => 0.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
|
||||
case (TopType, _) | (_, BottomType) => 1.0
|
||||
case (BottomType, _) | (_, TopType) => -1.0
|
||||
|
||||
// Collections
|
||||
case (x: ArrayType, y: ArrayType) => apply(x.element, y.element)
|
||||
case (x: ArrayType, y: StreamType) => apply(x.element, y.element)
|
||||
case (x: ArrayType, y: OptionType) => apply(x.element, y.element)
|
||||
case (x: OptionType, y: OptionType) => apply(x.element, y.element)
|
||||
case (x: OptionType, y: StreamType) => apply(x.element, y.element)
|
||||
case (x: OptionType, y: ArrayType) => apply(x.element, y.element)
|
||||
case (x: StreamType, y: StreamType) => apply(x.element, y.element)
|
||||
case (lnt: AbilityType, rnt: AbilityType) => compareNamed(lnt.fields, rnt.fields)
|
||||
case (lnt: StructType, rnt: StructType) => compareNamed(lnt.fields, rnt.fields)
|
||||
// Collections
|
||||
case (x: ArrayType, y: ArrayType) => apply(x.element, y.element)
|
||||
case (x: ArrayType, y: StreamType) => apply(x.element, y.element)
|
||||
case (x: ArrayType, y: OptionType) => apply(x.element, y.element)
|
||||
case (x: OptionType, y: OptionType) => apply(x.element, y.element)
|
||||
case (x: OptionType, y: StreamType) => apply(x.element, y.element)
|
||||
case (x: OptionType, y: ArrayType) => apply(x.element, y.element)
|
||||
case (x: StreamType, y: StreamType) => apply(x.element, y.element)
|
||||
case (lnt: AbilityType, rnt: AbilityType) => compareNamed(lnt.fields, rnt.fields)
|
||||
case (lnt: StructType, rnt: StructType) => compareNamed(lnt.fields, rnt.fields)
|
||||
|
||||
// Products
|
||||
case (l: ProductType, r: ProductType) => compareProducts(l, r)
|
||||
// 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
|
||||
|
||||
// Arrows
|
||||
case (ArrowType(ldom, lcodom), ArrowType(rdom, rcodom)) =>
|
||||
val cmpDom = apply(ldom, rdom)
|
||||
val cmpCodom = apply(lcodom, rcodom)
|
||||
// Products
|
||||
case (l: ProductType, r: ProductType) => compareProducts(l, r)
|
||||
|
||||
if (cmpDom == 0 && cmpCodom == 0) 0
|
||||
else if (cmpDom <= 0 && cmpCodom >= 0) 1.0
|
||||
else if (cmpDom >= 0 && cmpCodom <= 0) -1.0
|
||||
else NaN
|
||||
// Arrows
|
||||
case (ArrowType(ldom, lcodom), ArrowType(rdom, rcodom)) =>
|
||||
val cmpDom = apply(ldom, rdom)
|
||||
val cmpCodom = apply(lcodom, rcodom)
|
||||
|
||||
case _ =>
|
||||
Double.NaN
|
||||
}
|
||||
if (cmpDom == 0 && cmpCodom == 0) 0
|
||||
else if (cmpDom <= 0 && cmpCodom >= 0) 1.0
|
||||
else if (cmpDom >= 0 && cmpCodom <= 0) -1.0
|
||||
else NaN
|
||||
|
||||
case _ =>
|
||||
Double.NaN
|
||||
}
|
||||
|
||||
implicit val partialOrder: PartialOrder[Type] =
|
||||
PartialOrder.from(CompareTypes.apply)
|
||||
|
@ -172,22 +172,30 @@ object ScalarType {
|
||||
val string = ScalarType("string")
|
||||
|
||||
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 number = signed ++ unsigned
|
||||
val integer = signed ++ unsigned
|
||||
val number = float ++ integer
|
||||
val all = number ++ Set(bool, string)
|
||||
}
|
||||
|
||||
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 {
|
||||
val float = LiteralType(ScalarType.float, "float")
|
||||
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 bool = LiteralType(Set(ScalarType.bool), "bool")
|
||||
val string = LiteralType(Set(ScalarType.string), "string")
|
||||
|
||||
def forInt(n: Int): LiteralType = if (n < 0) signed else unsigned
|
||||
}
|
||||
|
||||
sealed trait BoxType extends DataType {
|
||||
@ -234,7 +242,8 @@ sealed trait NamedType extends Type {
|
||||
}
|
||||
|
||||
// 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 =
|
||||
s"$name{${fields.map(_.toString).toNel.toList.map(kv => kv._1 + ": " + kv._2).mkString(", ")}}"
|
||||
@ -244,11 +253,11 @@ case class StructType(name: String, fields: NonEmptyMap[String, Type]) extends D
|
||||
case class AbilityType(name: String, fields: NonEmptyMap[String, Type]) extends NamedType {
|
||||
|
||||
lazy val arrows: Map[String, ArrowType] = fields.toNel.collect {
|
||||
case (name, at@ArrowType(_, _)) => (name, at)
|
||||
case (name, at @ ArrowType(_, _)) => (name, at)
|
||||
}.toMap
|
||||
|
||||
lazy val abilities: List[(String, AbilityType)] = fields.toNel.collect {
|
||||
case (name, at@AbilityType(_, _)) => (name, at)
|
||||
case (name, at @ AbilityType(_, _)) => (name, at)
|
||||
}
|
||||
|
||||
lazy val variables: List[(String, Type)] = fields.toNel.filter {
|
||||
|
@ -30,8 +30,6 @@ case class UniteTypes(scalarsCombine: ScalarsCombine.T) extends Monoid[Type]:
|
||||
|
||||
override def combine(a: Type, b: Type): Type =
|
||||
(a, b) match {
|
||||
case _ if CompareTypes(a, b) == 0.0 => a
|
||||
|
||||
case (ap: ProductType, bp: ProductType) =>
|
||||
combineProducts(ap, bp)
|
||||
|
||||
@ -75,8 +73,7 @@ case class UniteTypes(scalarsCombine: ScalarsCombine.T) extends Monoid[Type]:
|
||||
case 1.0 => a
|
||||
case -1.0 => b
|
||||
case 0.0 => a
|
||||
case _ =>
|
||||
TopType
|
||||
case _ => TopType
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user