From e115c39b67f63f10d8d5171190297939b51490be Mon Sep 17 00:00:00 2001 From: Remy <45566104+remyhaemmerle-da@users.noreply.github.com> Date: Tue, 4 Jun 2019 17:50:02 +0200 Subject: [PATCH] make colon valid fro party literals + spec cleanup (#1467) --- daml-lf/archive/da/daml_lf_1.proto | 2 +- .../daml/lf/data/DecimalModule.scala | 8 +- .../com/digitalasset/daml/lf/data/Ref.scala | 2 +- .../digitalasset/daml/lf/data/RefTest.scala | 90 +++++++------------ .../daml/lf/speedy/SBuiltinTest.scala | 2 +- daml-lf/spec/daml-lf-1.rst | 66 +++++++++----- .../daml/lf/value/ValueGenerators.scala | 4 +- 7 files changed, 86 insertions(+), 88 deletions(-) diff --git a/daml-lf/archive/da/daml_lf_1.proto b/daml-lf/archive/da/daml_lf_1.proto index be6f5f8d13..a4b20f0443 100644 --- a/daml-lf/archive/da/daml_lf_1.proto +++ b/daml-lf/archive/da/daml_lf_1.proto @@ -446,7 +446,7 @@ message PrimLit { sfixed64 timestamp = 5; // Party literal ('LitParty') - // *Must be a SimpleString* + // *Must be a PartyId string* string party = 7; // Date literal ('Date') diff --git a/daml-lf/data/src/main/scala/com/digitalasset/daml/lf/data/DecimalModule.scala b/daml-lf/data/src/main/scala/com/digitalasset/daml/lf/data/DecimalModule.scala index 0d15b2e3fc..d8d7779e1d 100644 --- a/daml-lf/data/src/main/scala/com/digitalasset/daml/lf/data/DecimalModule.scala +++ b/daml-lf/data/src/main/scala/com/digitalasset/daml/lf/data/DecimalModule.scala @@ -27,10 +27,12 @@ abstract class DecimalModule { cast(BigDecimal(new java.math.BigDecimal(x, MathContext.UNLIMITED))) // we use these to compare only, therefore set the precision to unlimited to make sure - // we can compare every number we're given - val max: T = unlimitedBigDecimal("9999999999999999999999999999.9999999999") + // we can compare every number we're given. + private val max: BigDecimal = unlimitedBigDecimal("9999999999999999999999999999.9999999999") + private val min: BigDecimal = unlimitedBigDecimal("-9999999999999999999999999999.9999999999") - val min: T = unlimitedBigDecimal("-9999999999999999999999999999.9999999999") + val MaxValue: T = assertFromBigDecimal(max) + val MinValue: T = assertFromBigDecimal(min) /** Checks that a `T` falls between `min` and `max`, and * round the number according to `scale`. Note that it does _not_ diff --git a/daml-lf/data/src/main/scala/com/digitalasset/daml/lf/data/Ref.scala b/daml-lf/data/src/main/scala/com/digitalasset/daml/lf/data/Ref.scala index ad433cbeb2..96b62f3b0c 100644 --- a/daml-lf/data/src/main/scala/com/digitalasset/daml/lf/data/Ref.scala +++ b/daml-lf/data/src/main/scala/com/digitalasset/daml/lf/data/Ref.scala @@ -138,7 +138,7 @@ object Ref { underscore. We use them to represent [PackageId]s and [Party] literals. In this way, we avoid empty identifiers, escaping problems, and other similar pitfalls. */ - val Party = ConcatenableMatchingStringModule("-_ ".contains(_)) + val Party = ConcatenableMatchingStringModule(":-_ ".contains(_)) type Party = Party.T /** Reference to a package via a package identifier. The identifier is the ascii7 diff --git a/daml-lf/data/src/test/scala/com/digitalasset/daml/lf/data/RefTest.scala b/daml-lf/data/src/test/scala/com/digitalasset/daml/lf/data/RefTest.scala index de460a7a2c..41cdff5cc1 100644 --- a/daml-lf/data/src/test/scala/com/digitalasset/daml/lf/data/RefTest.scala +++ b/daml-lf/data/src/test/scala/com/digitalasset/daml/lf/data/RefTest.scala @@ -73,80 +73,58 @@ class RefTest extends FreeSpec with Matchers { "Party and PackageId" - { - val simpleChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ " - - "accepts simple characters" in { - for (c <- simpleChars) { - Party.fromString(s"the character $c is simple") shouldBe 'right - PackageId.fromString(s"the character $c is simple") shouldBe 'right - } - } - - "rejects the empty string" in { - Party.fromString("") shouldBe 'left - PackageId.fromString("") shouldBe 'left - } - - "rejects non simple US-ASCII characters" in { - for (c <- '\u0001' to '\u007f' if !simpleChars.contains(c)) { - Party.fromString(s"the US-ASCII character $c is not simple") shouldBe 'left - PackageId.fromString(s"the US-ASCII character $c is not simple") shouldBe 'left - } - } - - "rejects no US-ASCII characters" in { - for (c <- '\u0080' to '\u00ff') { - Party.fromString(s"the character '$c' is not US-ASCII") shouldBe 'left - PackageId.fromString(s"the character '$c' is not US-ASCII") shouldBe 'left - } - for (s <- List( - "español", - "東京", - "Λ (τ : ⋆) (σ: ⋆ → ⋆). λ (e : ∀ (α : ⋆). σ α) → (( e @τ ))" - )) { - Party.fromString(s) shouldBe 'left - PackageId.fromString(s) shouldBe 'left - } - } - } - - "LedgerString" - { - + val packageIdChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ " + val partyIdChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:-_ " val ledgerStringChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._:-# " - "accepts simple characters" in { - for (c <- ledgerStringChars) { - LedgerString.fromString(s"the_character_${c}_should_be_accepted") shouldBe 'right - } - } + def makeString(c: Char): String = s"the character $c is not US-ASCII" "rejects the empty string" in { + PackageId.fromString("") shouldBe 'left + Party.fromString("") shouldBe 'left LedgerString.fromString("") shouldBe 'left } - "reject too long string" in { - LedgerString.fromString("a" * 255) shouldBe 'right - LedgerString.fromString("a" * 256) shouldBe 'left - LedgerString.fromString("a" * 500) shouldBe 'left - } - - "rejects non allowed US-ASCII characters" in { - for (c <- '\u0001' to '\u007f' if !ledgerStringChars.contains(c)) { - LedgerString.fromString(s"the_character_${c}_should_be_rejected") shouldBe 'left + "treats US-ASCII characters as expected" in { + for (c <- '\u0001' to '\u007f') { + val s = makeString(c) + PackageId.fromString(s) shouldBe (if (packageIdChars.contains(c)) 'right else 'left) + Party.fromString(s) shouldBe (if (partyIdChars.contains(c)) 'right else 'left) + LedgerString.fromString(s) shouldBe (if (ledgerStringChars.contains(c)) 'right else 'left) } } "rejects no US-ASCII characters" in { + + val negativeTestCase = makeString('a') + + PackageId.fromString(negativeTestCase) shouldBe 'right + Party.fromString(negativeTestCase) shouldBe 'right + LedgerString.fromString(negativeTestCase) shouldBe 'right + for (c <- '\u0080' to '\u00ff') { - LedgerString.fromString(s"the_character_${c}_should_be_rejected") shouldBe 'left + val positiveTestCase = makeString(c) + PackageId.fromString(positiveTestCase) shouldBe 'left + Party.fromString(positiveTestCase) shouldBe 'left + LedgerString.fromString(positiveTestCase) shouldBe 'left } - for (s <- List( + for (positiveTestCase <- List( "español", "東京", "Λ (τ : ⋆) (σ: ⋆ → ⋆). λ (e : ∀ (α : ⋆). σ α) → (( e @τ ))" )) { - LedgerString.fromString(s) shouldBe 'left + Party.fromString(positiveTestCase) shouldBe 'left + PackageId.fromString(positiveTestCase) shouldBe 'left } } + + "LedgerString should reject too long strings" in { + val negativeTestCase = "a" * 255 + val positiveTestCase1 = "a" * 256 + val positiveTestCase2 = "a" * 500 + LedgerString.fromString(negativeTestCase) shouldBe 'right + LedgerString.fromString(positiveTestCase1) shouldBe 'left + LedgerString.fromString(positiveTestCase2) shouldBe 'left + } } } diff --git a/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/SBuiltinTest.scala b/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/SBuiltinTest.scala index f08bc7a106..8ad9b1ff82 100644 --- a/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/SBuiltinTest.scala +++ b/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/SBuiltinTest.scala @@ -188,7 +188,7 @@ class SBuiltinTest extends FreeSpec with Matchers with TableDrivenPropertyChecks "Decimal operations" - { - val maxDecimal = Decimal.max + val maxDecimal = Decimal.MaxValue val minPosDecimal = BigDecimal("0000000000000000000000000000.0000000001") val bigBigDecimal = BigDecimal("8765432109876543210987654321.0987654321") diff --git a/daml-lf/spec/daml-lf-1.rst b/daml-lf/spec/daml-lf-1.rst index 3b29eaccc8..d7d98f3d50 100644 --- a/daml-lf/spec/daml-lf-1.rst +++ b/daml-lf/spec/daml-lf-1.rst @@ -315,40 +315,52 @@ any code point with an integer value in the range from (bounds included). -Then, we define the so-called *simple strings*. Simple strings are -non-empty US-ASCII strings built with letters, digits, space, minus -and, underscore. We use them in instances when we want to avoid empty -identifiers, escaping problems, and other similar pitfalls. :: +Then, we define the so-called *PackageId strings* and *PartyId +strings*. Those are non-empty strings built with a limited set of +US-ASCII characters (See the rules `PackageIdChar` and `PartyIdChar` +below for the exact sets of characters). We use those string in +instances when we want to avoid empty identifiers, escaping problems, +and other similar pitfalls. :: - Simple strings - SimpleString ::= ' SimpleChars ' -- SimpleString + PackageId strings + PackageIdString ::= ' PackageIdChars ' -- PackageIdString - Sequences of simple characters - SimpleChars ::= SimpleChar -- SimpleChars - | SimpleChars SimpleChar + Sequences of PackageId character + PackageIdChars ::= PackageIdChar -- PackageIdChars + | PackageIdChars PackageIdChar - Simple characters - SimpleChar ∈ [a-zA-Z0-9\-_ ] -- SimpleChar + PackageId character + PackageIdChar ∈ [a-zA-Z0-9\-_ ] -- PackageIdChar + + PartyId strings + PartyIdString ::= ' PartyIdChars ' -- PartyIdString + + Sequences of PartyId character + PartyIdChars ::= PartyIdChar -- PartyIdChars + | PartyIdChars PartyIdChar + + PartyId character + PartyIdChar ∈ [a-zA-Z0-9:\-_ ] -- PartyIdChar We can now define all the literals that a program can handle:: 64-bits integer literals: - LitInt64 ∈ (-?)[0-9]+ -- LitInt64: + LitInt64 ∈ (-?)[0-9]+ -- LitInt64: Decimal literals: LitDecimal ∈ ([+-]?)\d{1,28}(.[0-9]\d{1-10})? -- LitDecimal Date literals: - LitDate ∈ \d{4}-\d{4}-\d{4} -- LitDate + LitDate ∈ \d{4}-\d{4}-\d{4} -- LitDate UTC timestamp literals: LitTimestamp ∈ \d{4}-\d{4}-\d{4}T\d{2}:\d{2}:\d{2}(.\d{1,3})?Z - -- LitTimestamp + -- LitTimestamp UTF8 string literals: - LitText ::= String -- LitText + LitText ::= String -- LitText Party literals: - LitParty ::= SimpleString -- LitParty + LitParty ::= PartyIdString -- LitParty The literals represent actual DAML-LF values: @@ -400,8 +412,8 @@ In the following, we will use identifiers to represent *built-in functions*, term and type *variable names*, record and tuple *field names*, *variant constructors*, and *template choices*. On the other hand, we will use names to represent *type constructors*, *value -references*, and *module names*. Finally, we will use simple strings -as *package identifiers*. :: +references*, and *module names*. Finally, we will use PackageId +strings as *package identifiers*. :: Expression variables x, y, z ::= Ident -- VarExp @@ -434,7 +446,7 @@ as *package identifiers*. :: cid -- ContractId Package identifiers - pid ::= SimpleString -- PkgId + pid ::= PackageIdString -- PkgId We do not specify an explicit syntax for contract identifiers as it is not possible to refer to them statically within a program. In @@ -2033,12 +2045,18 @@ Decimal functions * ``MUL_DECIMAL : 'Decimal' → 'Decimal' → 'Decimal'`` - Multiplies the two decimals. Throws an error in case of overflow. + Multiplies the two decimals and rounds the result to the closest + multiple of ``10⁻¹⁰`` using `banker's rounding convention + `_. + Throws an error in case of overflow. * ``DIV_DECIMAL : 'Decimal' → 'Decimal' → 'Decimal'`` - Divides the first decimal by the second one. Throws an error in - case of overflow. + Divides the first decimal by the second one and rounds the result to + the closest multiple of ``10⁻¹⁰`` using `banker's rounding + convention + `_. Throws + an error in case of overflow. * ``ROUND_DECIMAL : 'Int64' → 'Decimal' → 'Decimal'`` @@ -2292,7 +2310,7 @@ Party functions Returns the string representation of the party. This function, together with ``FROM_TEXT_PARTY``, forms an isomorphism between - `simple strings `_ and parties. In other words, + `PartyId strings `_ and parties. In other words, the following equations hold:: ∀ p. FROM_TEXT_PARTY (TO_TEXT_PARTY p) = 'Some' p @@ -2303,7 +2321,7 @@ Party functions * ``FROM_TEXT_PARTY : 'Text' → 'Optional' 'Party'`` Given the string representation of the party, returns the party, - if the input string is a `simple string `_. + if the input string is a `PartyId strings `_. [*Available since version 1.2*] diff --git a/daml-lf/transaction-scalacheck/src/main/scala/com/digitalasset/daml/lf/value/ValueGenerators.scala b/daml-lf/transaction-scalacheck/src/main/scala/com/digitalasset/daml/lf/value/ValueGenerators.scala index e29b0e5c42..45584a4feb 100644 --- a/daml-lf/transaction-scalacheck/src/main/scala/com/digitalasset/daml/lf/value/ValueGenerators.scala +++ b/daml-lf/transaction-scalacheck/src/main/scala/com/digitalasset/daml/lf/value/ValueGenerators.scala @@ -93,8 +93,8 @@ object ValueGenerators { Gen .frequency( (1, Gen.const(BigDecimal("0.0"))), - (1, Gen.const(Decimal.max)), - (1, Gen.const(Decimal.min)), + (1, Gen.const(Decimal.MaxValue)), + (1, Gen.const(Decimal.MinValue)), (5, bd) ) .map(d => ValueDecimal(Decimal.assertFromBigDecimal(d)))