daml-lf: add FROM_TEXT_INT64 and FROM_TEXT_DECIMAL (#1407)

* daml-lf: add FROM_TEXT_INT64 and FROM_TEXT_DECIMAL

* Fix typos in Haskell

* cosmetic change

* Fix DAML-LF type checker

* Fix merging fallout
This commit is contained in:
Remy 2019-05-27 19:19:02 +02:00 committed by mergify[bot]
parent 53c5351144
commit 439613bee8
17 changed files with 166 additions and 9 deletions

View File

@ -221,8 +221,6 @@ data BuiltinExpr
| BEGreater !BuiltinType -- :: t -> t -> Bool, where t is the builtin type
| BEToText !BuiltinType -- :: t -> Text, where t is one of the builtin types
-- {Int64, Decimal, Text, Timestamp, Date, Party}
| BEPartyFromText -- :: Text -> Optional Party
| BEPartyToQuotedText -- :: Party -> Text
-- Decimal arithmetic
| BEAddDecimal -- :: Decimal -> Decimal -> Decimal, crashes on overflow
@ -266,6 +264,10 @@ data BuiltinExpr
| BEAppendText -- :: Text -> Text -> Text
| BEImplodeText -- :: List Text -> Text
| BESha256Text -- :: Text -> Text
| BEPartyFromText -- :: Text -> Optional Party
| BEInt64FromText -- :: Text -> Optional Int64
| BEDecimalFromText -- :: Text -> Optional Decimal
| BEPartyToQuotedText -- :: Party -> Text
| BETrace -- :: forall a. Text -> a -> a
| BEEqualContractId -- :: forall a. ContractId a -> ContractId a -> Bool

View File

@ -223,7 +223,9 @@ instance Pretty BuiltinExpr where
BESha256Text -> "SHA256_TEXT"
BETrace -> "TRACE"
BEEqualContractId -> "EQUAL_CONTRACT_ID"
BEPartyFromText -> "PARTY_FROM_TEXT"
BEPartyFromText -> "FROM_TEXT_PARTY"
BEInt64FromText -> "FROM_TEXT_INT64"
BEDecimalFromText -> "FROM_TEXT_DECIMAL"
BEPartyToQuotedText -> "PARTY_TO_QUOTED_TEXT"
BECoerceContractId -> "COERCE_CONTRACT_ID"
where

View File

@ -75,6 +75,9 @@ featureCoerceContractId = Feature "Coerce function for contract ids" version1_5
featureExerciseActorsOptional :: Feature
featureExerciseActorsOptional = Feature "Optional exercise actors" version1_5
featureNumberFromText :: Feature
featureNumberFromText = Feature "Number parsing functions" version1_5
supports :: Version -> Feature -> Bool
supports version feature = version >= featureMinVersion feature

View File

@ -198,6 +198,8 @@ decodeBuiltinFunction = pure . \case
LF1.BuiltinFunctionTO_TEXT_PARTY -> BEToText BTParty
LF1.BuiltinFunctionTO_TEXT_DATE -> BEToText BTDate
LF1.BuiltinFunctionFROM_TEXT_PARTY -> BEPartyFromText
LF1.BuiltinFunctionFROM_TEXT_INT64 -> BEInt64FromText
LF1.BuiltinFunctionFROM_TEXT_DECIMAL -> BEDecimalFromText
LF1.BuiltinFunctionTO_QUOTED_TEXT_PARTY -> BEPartyToQuotedText
LF1.BuiltinFunctionADD_DECIMAL -> BEAddDecimal

View File

@ -235,6 +235,8 @@ encodeBuiltinExpr = \case
BTParty -> builtin P.BuiltinFunctionTO_TEXT_PARTY
other -> error $ "BEToText unexpected type " <> show other
BEPartyFromText -> builtin P.BuiltinFunctionFROM_TEXT_PARTY
BEInt64FromText -> builtin P.BuiltinFunctionFROM_TEXT_INT64
BEDecimalFromText-> builtin P.BuiltinFunctionFROM_TEXT_DECIMAL
BEPartyToQuotedText -> builtin P.BuiltinFunctionTO_QUOTED_TEXT_PARTY
BEAddDecimal -> builtin P.BuiltinFunctionADD_DECIMAL

View File

@ -149,6 +149,8 @@ safetyStep = \case
BEEqualContractId -> Safe 2
BEPartyToQuotedText -> Safe 1
BEPartyFromText -> Safe 1
BEInt64FromText -> Safe 1
BEDecimalFromText -> Safe 1
BECoerceContractId -> Safe 1
ERecConF _ fs -> minimum (Safe 0 : map snd fs)
ERecProjF _ _ s -> s `min` Safe 0

View File

@ -161,6 +161,12 @@ typeOfBuiltin = \case
BEToText btype -> pure $ TBuiltin btype :-> TText
BEPartyToQuotedText -> pure $ TParty :-> TText
BEPartyFromText -> pure $ TText :-> TOptional TParty
BEInt64FromText -> do
checkFeature featureNumberFromText
pure $ TText :-> TOptional TInt64
BEDecimalFromText -> do
checkFeature featureNumberFromText
pure $ TText :-> TOptional TDecimal
BEAddDecimal -> pure $ tBinop TDecimal
BESubDecimal -> pure $ tBinop TDecimal
BEMulDecimal -> pure $ tBinop TDecimal

View File

@ -129,6 +129,10 @@ convertPrim _ "BEPartyToQuotedText" (TParty :-> TText) =
EBuiltin BEPartyToQuotedText
convertPrim _ "BEPartyFromText" (TText :-> TOptional TParty) =
EBuiltin BEPartyFromText
convertPrim version "BEInt64FromText" t@(TText :-> TOptional TInt64) =
whenRuntimeSupports version featureNumberFromText t $ EBuiltin BEInt64FromText
convertPrim version "BEDecimalFromText" t@(TText :-> TOptional TDecimal) =
whenRuntimeSupports version featureNumberFromText t $ EBuiltin BEDecimalFromText
-- Map operations

View File

@ -372,7 +372,9 @@ enum BuiltinFunction {
TO_TEXT_DATE = 71;
TO_QUOTED_TEXT_PARTY = 63; // legacy, remove in next major version
TO_TEXT_PARTY = 94; // *Available Since version 1.2*
FROM_TEXT_PARTY = 95; // *Available Since version 1.2*
FROM_TEXT_PARTY = 95; // *Available Since version 1.2*, was named FROM_TEXT_PARTY in 1.2, 1.3 and 1.4
FROM_TEXT_INT64 = 103; // *Available Since version 1.5*
FROM_TEXT_DECIMAL = 104; // *Available Since version 1.5*
SHA256_TEXT = 93; // *Available Since version 1.2*
DATE_TO_UNIX_DAYS = 72; // Date -> Int64
@ -400,7 +402,7 @@ enum BuiltinFunction {
COERCE_CONTRACT_ID = 102;
// Next id is 103. 102 is COERCE_CONTRACT_ID.
// Next id is 105. 104 is FROM_TEXT_DECIMAL.
}
// Builtin literals

View File

@ -185,6 +185,8 @@ final case class Compiler(packages: PackageId PartialFunction Package) {
case BToTextDate => SBToText
case BToQuotedTextParty => SBToQuotedTextParty
case BFromTextParty => SBFromTextParty
case BFromTextInt64 => SBFromTextInt64
case BFromTextDecimal => SBFromTextDecimal
case BSHA256Text => SBSHA256Text

View File

@ -233,6 +233,38 @@ object SBuiltin {
}
}
final case object SBFromTextInt64 extends SBuiltin(1) {
private val pattern = """[+-]?\d+""".r.pattern
def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
val s = args.get(0).asInstanceOf[SText].value
val int64 =
if (pattern.matcher(s).matches())
try {
Some(SInt64(java.lang.Long.parseLong(s)))
} catch {
case _: NumberFormatException =>
None
} else
None
machine.ctrl = CtrlValue(SOptional(int64))
}
}
final case object SBFromTextDecimal extends SBuiltin(1) {
private val pattern = """[+-]?[0-9]+(\.[0-9]+)?""".r.pattern
def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
val s = args.get(0).asInstanceOf[SText].value
val decimal =
if (pattern.matcher(s).matches())
Decimal.fromBigDecimal(BigDecimal(s)).fold(_ => None, x => Some(SDecimal(x)))
else
None
machine.ctrl = CtrlValue(SOptional(decimal))
}
}
final case object SBSHA256Text extends SBuiltin(1) {
def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
machine.ctrl = CtrlValue(args.get(0) match {

View File

@ -738,7 +738,7 @@ class SBuiltinTest extends FreeSpec with Matchers with TableDrivenPropertyChecks
}
}
"TO_TEXT_PARTY & TO_QUOTED_TEXT_PARTY & FROM_TEXT_PARTY" - {
"Text Operations" - {
"TO_QUOTED_TEXT_PARTY single quotes" in {
eval(e"TO_QUOTED_TEXT_PARTY 'alice'") shouldBe Right(SText("'alice'"))
}
@ -752,7 +752,99 @@ class SBuiltinTest extends FreeSpec with Matchers with TableDrivenPropertyChecks
SOptional(Some(SParty(Ref.Party.assertFromString("alice")))))
eval(e"""FROM_TEXT_PARTY "bad%char" """) shouldBe Right(SOptional(None))
}
"FROM_TEXT_INT64" in {
val positiveTestCases =
Table(
"strings",
"-9223372036854775808",
"-9223372036854775807",
"-22",
"0",
"42",
"9223372036854775806",
"9223372036854775807",
"+9223372036854775807",
"01",
"0" * 20 + "42",
"-003",
)
val negativeTestCases =
Table(
"strings",
"pi",
"0x11",
"1.0",
"2.",
"1L",
"+-1",
"9223372036854775808",
"9223372036854775809",
"-9223372036854775809",
"-9223372036854775810",
"1" * 20
)
forEvery(positiveTestCases) { s =>
eval(e"""FROM_TEXT_INT64 "$s"""") shouldBe Right(SOptional(Some(SInt64(s.toLong))))
}
forEvery(negativeTestCases) { s =>
eval(e"""FROM_TEXT_INT64 "$s"""") shouldBe Right(SOptional(None))
}
}
}
"FROM_TEXT_DECIMAL" in {
val positiveTestCases =
Table(
"strings" -> "canonical string",
("9" * 28 + "." + "9" * 10) -> ("9" * 28 + "." + "9" * 10),
("0" * 20 + "1" * 28) -> ("0" * 20 + "1" * 28),
"161803398.87499" -> "161803398.87499",
"3.1415926536" -> "3.1415926536",
"2.7182818285" -> "2.7182818285",
"0.0000000001" -> "0.0000000001",
"0.0005" + "0" * 20 -> "0.0005",
"+0.0" -> "0.0",
"0.0" -> "0.0",
"0" -> "0.0",
"-0" -> "0.0",
"42" -> "42.0",
"-0.0005" + "0" * 20 -> "-0.0005",
"-0.0000000001" -> "-0.0000000001",
"-2.7182818285" -> "-2.7182818285",
"-3.1415926536" -> "-3.1415926536",
"-161803398.87499" -> "-161803398.87499",
("-" + "0" * 20 + "1" * 28) -> ("-" + "1" * 28),
("-" + "9" * 28 + "." + "9" * 10) -> ("-" + "9" * 28 + "." + "9" * 10)
)
val negativeTestCases =
Table(
"strings",
"pi",
"0x11",
"1E10",
"2.",
"1L",
"+-1",
"1" * 29,
"-" + "1" * 29,
"+" + "1" * 29,
"1" * 29,
"0." + "0" * 10 + "1",
"42" + "0" * 24 + "2019",
)
forEvery(positiveTestCases) { (input, expected) =>
eval(e"""FROM_TEXT_DECIMAL "$input"""") shouldBe Right(
SOptional(
Some(SDecimal(Decimal.assertFromBigDecimal(BigDecimal(expected).setScale(10))))))
}
forEvery(negativeTestCases) { s =>
eval(e"""FROM_TEXT_DECIMAL "$s"""") shouldBe Right(SOptional(None))
}
}
}
"Debugging builtins" - {

View File

@ -340,6 +340,8 @@ object Ast {
final case object BToTextDate extends BuiltinFunction(1) // :: Date -> Text
final case object BToQuotedTextParty extends BuiltinFunction(1) // :: Party -> Text
final case object BFromTextParty extends BuiltinFunction(1) // :: Text -> Optional Party
final case object BFromTextInt64 extends BuiltinFunction(1) // :: Text -> Optional Int64
final case object BFromTextDecimal extends BuiltinFunction(1) // :: Text -> Optional Decimal
final case object BSHA256Text extends BuiltinFunction(arity = 1) // :: Text -> Text

View File

@ -729,6 +729,8 @@ object DecodeV1 {
TO_TEXT_TEXT -> (BToTextText -> "0"),
TO_QUOTED_TEXT_PARTY -> (BToQuotedTextParty -> "0"),
FROM_TEXT_PARTY -> (BFromTextParty -> "2"),
FROM_TEXT_INT64 -> (BFromTextInt64 -> "5"),
FROM_TEXT_DECIMAL -> (BFromTextInt64 -> "5"),
SHA256_TEXT -> (BSHA256Text -> "2"),
DATE_TO_UNIX_DAYS -> (BDateToUnixDays -> "0"),
EXPLODE_TEXT -> (BExplodeText -> "0"),

View File

@ -229,6 +229,8 @@ private[parser] object ExprParser {
"TO_TEXT_DATE" -> BToTextDate,
"TO_QUOTED_TEXT_PARTY" -> BToQuotedTextParty,
"FROM_TEXT_PARTY" -> BFromTextParty,
"FROM_TEXT_INT64" -> BFromTextInt64,
"FROM_TEXT_DECIMAL" -> BFromTextDecimal,
"ERROR" -> BError,
"LESS_INT64" -> BLessInt64,
"LESS_DECIMAL" -> BLessDecimal,

View File

@ -1112,7 +1112,7 @@ for the ``DefTemplate`` rule). ::
Γ ⊢ 'no_key'
⊢ₛ τ Γ ⊢ eₖ : τ
⊢ₖ eₖ [DAML-LF 1.3]
⊢ₖ eₖ [DAML-LF = 1.3]
ε ⊢ eₘ : τ → 'List' 'Party'
——————————————————————————————————————————————————————————————— KeyDefSome
Γ ⊢ 'key' τ eₖ eₘ
@ -1962,7 +1962,6 @@ Int64 functions
Returns the decimal representation of the integer as a string.
Decimal functions
~~~~~~~~~~~~~~~~~
@ -2024,7 +2023,6 @@ Decimal functions
Returns the decimal string representation of the decimal.
String functions
~~~~~~~~~~~~~~~~

View File

@ -121,6 +121,8 @@ private[validation] object Typing {
BSHA256Text -> (TText ->: TText),
BToQuotedTextParty -> (TParty ->: TText),
BFromTextParty -> (TText ->: TOptional(TParty)),
BFromTextInt64 -> (TText ->: TOptional(TInt64)),
BFromTextDecimal -> (TText ->: TOptional(TDecimal)),
BError -> TForall(alpha.name -> KStar, TText ->: alpha),
// ComparisonsA
BLessInt64 -> tComparison(BTInt64),