From 21e278aa4654aa67426abd43dc6d7088bce2c2e8 Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Thu, 27 May 2021 14:40:18 -0500 Subject: [PATCH 01/20] Add some string-ascii and string-utf8 types to the docs --- src/vm/docs/mod.rs | 83 ++++++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 29 deletions(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index 6164b6e053..cec615c64e 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -463,31 +463,46 @@ inputted value.", }; const MAP_API: SpecialAPI = SpecialAPI { - input_type: "Function(A, B, ..., N) -> X, (list A1 A2 ... Am), (list B1 B2 ... Bm), ..., (list N1 N2 ... Nm)", + input_type: "Function((buff 1)_1, ..., (buff 1)_N) -> X, (buff m)_1, ..., (buff m)_N +Function(A, B, ..., N) -> X, (list A1 A2 ... Am), (list B1 B2 ... Bm), ..., (list N1 N2 ... Nm) +Function((string-ascii 1)_1, ..., (string-ascii 1)_N) -> X, (string-ascii m)_1, ..., (string-ascii m)_N +Function((string-utf8 1)_1, ..., (string-utf8 1)_N) -> X, (string-utf8 m)_1, ..., (string-utf8 m)_N", output_type: "(list X)", - signature: "(map func list-A list-B ... list-N)", + signature: "(map func sequence-A sequence-B ... sequence-N)", description: "The `map` function applies the input function `func` to each element of the -input lists, and outputs a list containing the _outputs_ from those function applications.", +input sequences (where each sequence is either `buff`, `list`, `string-ascii` or `string-utf8`), and outputs a +_list_ of the same type containing the _outputs_ from those function applications. Note that, +no matter what kind of sequences the inputs are, the output is always a list.", example: " (map not (list true false true false)) ;; Returns (false true false true) (map + (list 1 2 3) (list 1 2 3) (list 1 2 3)) ;; Returns (3 6 9)", }; const FILTER_API: SpecialAPI = SpecialAPI { - input_type: "Function(A) -> bool, (list A)", - output_type: "(list A)", - signature: "(filter func list)", + input_type: "Function((buff 1)) -> bool, buff +Function(A) -> bool, (list A) +Function((string-ascii 1)) -> bool, string-ascii +Function((string-utf8 1)) -> bool, string-utf8", + output_type: "buff +(list A) +string-ascii +string-utf8", + signature: "(filter func sequence)", description: "The `filter` function applies the input function `func` to each element of the -input list, and returns the same list with any elements removed for which the `func` returned `false`.", +input sequence (which is either a `buff`, `list`, `string-ascii` or `string-utf8`), and returns the +same list with any elements removed for which the `func` returned `false`.", example: "(filter not (list true false true false)) ;; Returns (false false)" }; const FOLD_API: SpecialAPI = SpecialAPI { - input_type: "Function(A, B) -> B, (list A), B", + input_type: "Function((buff 1), B) -> B, buff, B +Function(A, B) -> B, (list A), B +Function((string-ascii 1), B) -> B, string-ascii, B +Function((string-utf8 1), B) -> B, string-utf8, B", output_type: "B", - signature: "(fold func list initial-value)", - description: "The `fold` special form applies the input function `func` to each element of the -input list _and_ the output of the previous application of the `fold` function. When invoked on + signature: "(fold func sequence initial-value)", + description: "The `fold` function applies the input function `func` to each element of the +input sequence (buff, list or string) _and_ the output of the previous application of the `fold` function. When invoked on the first list element, it uses the `initial-value` as the second input. `fold` returns the last value returned by the successive applications. Note that the first argument is not evaluated thus has to be a literal function name.", @@ -501,11 +516,15 @@ has to be a literal function name.", }; const CONCAT_API: SpecialAPI = SpecialAPI { - input_type: "(buff, buff)|(list, list)", - output_type: "buff|list", - signature: "(concat buff-a buff-b)", - description: "The `concat` function takes two buffers or two lists with the same entry type, -and returns a concatenated buffer or list of the same entry type, with max_len = max_len_a + max_len_b.", + input_type: " +buff, buff +list, list +string-ascii, string-ascii +string-utf8, string-utf8", + output_type: "buff|list|string-ascii|string-utf8", + signature: "(concat sequence-a sequence-b)", + description: "The `concat` function takes two sequences (buffers, lists or strings) with the same entry type, +and returns a concatenated sequence of the same type, with seq_len = seq_len_a + seq_len_b.", example: "(concat \"hello \" \"world\") ;; Returns \"hello world\"" }; @@ -519,36 +538,38 @@ and outputs a list of the same type with max_len += 1.", }; const ASSERTS_MAX_LEN_API: SpecialAPI = SpecialAPI { - input_type: "buff|list, uint", - output_type: "(optional buff|list)", - signature: "(as-max-len? buffer u10)", - description: "The `as-max-len?` function takes a length N (must be a literal) and a buffer or list argument, which must be typed as a list + input_type: "buff|list|string-ascii|string-utf8, uint", + output_type: "(optional buff|list|string-ascii|string-utf8)", + signature: "(as-max-len? sequence u10)", + description: "The `as-max-len?` function takes a length N (must be a literal) and a sequence +(buffer, list or string) argument, which must be typed as a list or buffer of length M and outputs that same list or buffer, but typed with max length N. This function returns an optional type with the resulting sequence. If the input sequence is less than or equal to the supplied max-len, it returns `(some )`, otherwise it returns `none`.", example: "(as-max-len? (list 2 2 2) u3) ;; Returns (some (2 2 2)) -(as-max-len? (list 1 2 3) u2) ;; Returns none" +(as-max-len? (list 1 2 3) u2) ;; Returns none +(as-max-len? \"hello\" u10) ;; Returns (some \"hello\")" }; const LEN_API: SpecialAPI = SpecialAPI { - input_type: "buff|list", + input_type: "buff|list|string-ascii|string-utf8", output_type: "uint", - signature: "(len buffer)", - description: "The `len` function returns the length of a given buffer or list.", + signature: "(len sequence)", + description: "The `len` function returns the length of a given buffer, list or string.", example: "(len \"blockstack\") ;; Returns u10 (len (list 1 2 3 4 5)) ;; Returns u5 ", }; const ELEMENT_AT_API: SpecialAPI = SpecialAPI { - input_type: "buff|list A, uint", - output_type: "(optional buff|A)", + input_type: "buff|list|string-ascii|string-utf8 A, uint", + output_type: "(optional (buff 1)|A|(string-ascii 1)|(string-utf8 1))", signature: "(element-at sequence index)", description: "The `element-at` function returns the element at `index` in the provided sequence. If `index` is greater than or equal to `(len sequence)`, this function returns `none`. -For strings and buffers, this function will return 1-length strings or buffers.", +For strings or buffers, this function will return 1-length strings or buffers.", example: "(element-at \"blockstack\" u5) ;; Returns (some \"s\") (element-at (list 1 2 3 4 5) u5) ;; Returns none (element-at (list 1 2 3 4 5) (+ u1 u2)) ;; Returns (some 4) @@ -558,11 +579,15 @@ For strings and buffers, this function will return 1-length strings or buffers." }; const INDEX_OF_API: SpecialAPI = SpecialAPI { - input_type: "buff|list A, buff|A", + input_type: "buff, (buff 1) +list A, A +string-ascii (string-ascii 1) +string-utf8 (string-utf8 1)", output_type: "(optional uint)", signature: "(index-of sequence item)", description: "The `index-of` function returns the first index at which `item` can be -found in the provided sequence (using `is-eq` checks). +found, using `is-eq` checks, in the provided sequence, which is either +`buff`, `list`, `string-ascii` or `string-utf8`. If this item is not found in the sequence (or an empty string/buffer is supplied), this function returns `none`.", From c6a4662504cc6816d99f7bbe8a8e4f327c6606c1 Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Fri, 28 May 2021 09:18:48 -0500 Subject: [PATCH 02/20] ran format --- src/vm/docs/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index cec615c64e..3afca913a7 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -491,7 +491,7 @@ string-utf8", description: "The `filter` function applies the input function `func` to each element of the input sequence (which is either a `buff`, `list`, `string-ascii` or `string-utf8`), and returns the same list with any elements removed for which the `func` returned `false`.", - example: "(filter not (list true false true false)) ;; Returns (false false)" + example: "(filter not (list true false true false)) ;; Returns (false false)", }; const FOLD_API: SpecialAPI = SpecialAPI { From 2d8bb96b6edcf6c7dfc3473b9ce72ea1286e38e2 Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Wed, 2 Jun 2021 17:17:17 -0500 Subject: [PATCH 03/20] added some additional examples --- src/vm/docs/mod.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index f345c0d31e..f503beca1e 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -475,7 +475,12 @@ _list_ of the same type containing the _outputs_ from those function application no matter what kind of sequences the inputs are, the output is always a list.", example: " (map not (list true false true false)) ;; Returns (false true false true) -(map + (list 1 2 3) (list 1 2 3) (list 1 2 3)) ;; Returns (3 6 9)", +(map + (list 1 2 3) (list 1 2 3) (list 1 2 3)) ;; Returns (3 6 9) +(define-private (a-or-b (char (string-utf8 1))) (if (is-eq char u\"a\") u\"a\" u\"b\")) +(map a-or-b u\"aca\") ;; Returns (u\"a\" u\"b\" u\"a\") +(define-private (zero-or-one (char (buff 1))) (if (is-eq char 0x00) 0x00 0x01)) +(map zero-or-one 0x000102) ;; Returns (0x00 0x01 0x01) +", }; const FILTER_API: SpecialAPI = SpecialAPI { @@ -491,7 +496,13 @@ string-utf8", description: "The `filter` function applies the input function `func` to each element of the input sequence (which is either a `buff`, `list`, `string-ascii` or `string-utf8`), and returns the same list with any elements removed for which the `func` returned `false`.", - example: "(filter not (list true false true false)) ;; Returns (false false)", + example: " +(filter not (list true false true false)) ;; Returns (false false) +(define-private (is-a (char (string-utf8 1))) (is-eq char u\"a\")) +(filter is-a u\"acabd\") ;; Returns u\"aa\" +(define-private (is-zero (char (buff 1))) (is-eq char 0x00)) +(filter is-zero 0x00010002) ;; Returns 0x0000 +", }; const FOLD_API: SpecialAPI = SpecialAPI { From e3636e84597a4e17a4666ec1b772c8b5ada8db7a Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Wed, 2 Jun 2021 17:29:47 -0500 Subject: [PATCH 04/20] added all the different examples --- src/vm/docs/mod.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index f503beca1e..8d995f5975 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -523,7 +523,10 @@ has to be a literal function name.", (fold - (list 3 7 11) 2) ;; Returns 5 (define-private (concat-string (a (string-ascii 20)) (b (string-ascii 20))) (unwrap-panic (as-max-len? (concat a b) u20))) (fold concat-string \"cdef\" \"ab\") ;; Returns \"fedcab\" -(fold concat-string (list \"cd\" \"ef\") \"ab\") ;; Returns \"efcdab\"", +(fold concat-string (list \"cd\" \"ef\") \"ab\") ;; Returns \"efcdab\" +(define-private (concat-buff (a (buff 20)) (b (buff 20))) (unwrap-panic (as-max-len? (concat a b) u20))) +(fold concat-buff 0x03040506 0x0102) ;; Returns 0x060504030102 +", }; const CONCAT_API: SpecialAPI = SpecialAPI { @@ -560,7 +563,8 @@ This function returns an optional type with the resulting sequence. If the input or equal to the supplied max-len, it returns `(some )`, otherwise it returns `none`.", example: "(as-max-len? (list 2 2 2) u3) ;; Returns (some (2 2 2)) (as-max-len? (list 1 2 3) u2) ;; Returns none -(as-max-len? \"hello\" u10) ;; Returns (some \"hello\")" +(as-max-len? \"hello\" u10) ;; Returns (some \"hello\") +(as-max-len? 0x010203 u10) ;; Returns (some 0x010203)" }; const LEN_API: SpecialAPI = SpecialAPI { @@ -568,8 +572,10 @@ const LEN_API: SpecialAPI = SpecialAPI { output_type: "uint", signature: "(len sequence)", description: "The `len` function returns the length of a given buffer, list or string.", - example: "(len \"blockstack\") ;; Returns u10 + example: " +(len \"blockstack\") ;; Returns u10 (len (list 1 2 3 4 5)) ;; Returns u5 +(len 0x010203) ;; Returns u3 ", }; From afdd1bbbd31f34b35b1ddf95b98a2bf16457d080 Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Wed, 2 Jun 2021 17:32:26 -0500 Subject: [PATCH 05/20] added concat example --- src/vm/docs/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index 8d995f5975..3423d654fc 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -539,7 +539,10 @@ string-utf8, string-utf8", signature: "(concat sequence-a sequence-b)", description: "The `concat` function takes two sequences (buffers, lists or strings) with the same entry type, and returns a concatenated sequence of the same type, with seq_len = seq_len_a + seq_len_b.", - example: "(concat \"hello \" \"world\") ;; Returns \"hello world\"" + example: " +(concat \"hello \" \"world\") ;; Returns \"hello world\" +(concat 0x0102 0x0304) ;; Returns 0x01020304 +" }; const APPEND_API: SpecialAPI = SpecialAPI { From 85279288309aa7d964b0f60ad56524306fcf7e30 Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Fri, 4 Jun 2021 12:15:20 -0500 Subject: [PATCH 06/20] running format --- src/vm/docs/mod.rs | 77 ++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index 3423d654fc..9c74e188f3 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -473,14 +473,14 @@ Function((string-utf8 1)_1, ..., (string-utf8 1)_N) -> X, (string-utf8 m)_1, ... input sequences (where each sequence is either `buff`, `list`, `string-ascii` or `string-utf8`), and outputs a _list_ of the same type containing the _outputs_ from those function applications. Note that, no matter what kind of sequences the inputs are, the output is always a list.", - example: " + example: r#" (map not (list true false true false)) ;; Returns (false true false true) (map + (list 1 2 3) (list 1 2 3) (list 1 2 3)) ;; Returns (3 6 9) -(define-private (a-or-b (char (string-utf8 1))) (if (is-eq char u\"a\") u\"a\" u\"b\")) -(map a-or-b u\"aca\") ;; Returns (u\"a\" u\"b\" u\"a\") +(define-private (a-or-b (char (string-utf8 1))) (if (is-eq char u"a") u"a" u"b")) +(map a-or-b u"aca") ;; Returns (u"a" u"b" u"a") (define-private (zero-or-one (char (buff 1))) (if (is-eq char 0x00) 0x00 0x01)) (map zero-or-one 0x000102) ;; Returns (0x00 0x01 0x01) -", +"#, }; const FILTER_API: SpecialAPI = SpecialAPI { @@ -494,15 +494,15 @@ string-ascii string-utf8", signature: "(filter func sequence)", description: "The `filter` function applies the input function `func` to each element of the -input sequence (which is either a `buff`, `list`, `string-ascii` or `string-utf8`), and returns the +input sequence (where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`), and returns the same list with any elements removed for which the `func` returned `false`.", - example: " + example: r#" (filter not (list true false true false)) ;; Returns (false false) -(define-private (is-a (char (string-utf8 1))) (is-eq char u\"a\")) -(filter is-a u\"acabd\") ;; Returns u\"aa\" +(define-private (is-a (char (string-utf8 1))) (is-eq char u"a")) +(filter is-a u"acabd") ;; Returns u"aa" (define-private (is-zero (char (buff 1))) (is-eq char 0x00)) (filter is-zero 0x00010002) ;; Returns 0x0000 -", +"#, }; const FOLD_API: SpecialAPI = SpecialAPI { @@ -513,20 +513,21 @@ Function((string-utf8 1), B) -> B, string-utf8, B", output_type: "B", signature: "(fold func sequence initial-value)", description: "The `fold` function applies the input function `func` to each element of the -input sequence (buff, list or string) _and_ the output of the previous application of the `fold` function. When invoked on +input sequence (where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`) _and_ the output of the previous application of the `fold` function. When invoked on the first list element, it uses the `initial-value` as the second input. `fold` returns the last value returned by the successive applications. Note that the first argument is not evaluated thus has to be a literal function name.", - example: "(fold * (list 2 2 2) 1) ;; Returns 8 + example: r#" +(fold * (list 2 2 2) 1) ;; Returns 8 (fold * (list 2 2 2) 0) ;; Returns 0 ;; calculates (- 11 (- 7 (- 3 2))) (fold - (list 3 7 11) 2) ;; Returns 5 (define-private (concat-string (a (string-ascii 20)) (b (string-ascii 20))) (unwrap-panic (as-max-len? (concat a b) u20))) -(fold concat-string \"cdef\" \"ab\") ;; Returns \"fedcab\" -(fold concat-string (list \"cd\" \"ef\") \"ab\") ;; Returns \"efcdab\" +(fold concat-string "cdef" "ab") ;; Returns "fedcab" +(fold concat-string (list "cd" "ef") "ab") ;; Returns "efcdab" (define-private (concat-buff (a (buff 20)) (b (buff 20))) (unwrap-panic (as-max-len? (concat a b) u20))) (fold concat-buff 0x03040506 0x0102) ;; Returns 0x060504030102 -", +"#, }; const CONCAT_API: SpecialAPI = SpecialAPI { @@ -537,12 +538,12 @@ string-ascii, string-ascii string-utf8, string-utf8", output_type: "buff|list|string-ascii|string-utf8", signature: "(concat sequence-a sequence-b)", - description: "The `concat` function takes two sequences (buffers, lists or strings) with the same entry type, + description: "The `concat` function takes two sequences (where each sequence is either `buff`, `list`, `string-ascii` or `string-utf8`) with the same entry type, and returns a concatenated sequence of the same type, with seq_len = seq_len_a + seq_len_b.", - example: " -(concat \"hello \" \"world\") ;; Returns \"hello world\" + example: r#" +(concat "hello " "world") ;; Returns "hello world" (concat 0x0102 0x0304) ;; Returns 0x01020304 -" +"# }; const APPEND_API: SpecialAPI = SpecialAPI { @@ -558,28 +559,30 @@ const ASSERTS_MAX_LEN_API: SpecialAPI = SpecialAPI { input_type: "buff|list|string-ascii|string-utf8, uint", output_type: "(optional buff|list|string-ascii|string-utf8)", signature: "(as-max-len? sequence u10)", - description: "The `as-max-len?` function takes a length N (must be a literal) and a sequence -(buffer, list or string) argument, which must be typed as a list + description: "The `as-max-len?` function takes a length N (must be a literal) and a sequence argument +(where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`), which must be typed as a list or buffer of length M and outputs that same list or buffer, but typed with max length N. This function returns an optional type with the resulting sequence. If the input sequence is less than or equal to the supplied max-len, it returns `(some )`, otherwise it returns `none`.", - example: "(as-max-len? (list 2 2 2) u3) ;; Returns (some (2 2 2)) + example: r#" +(as-max-len? (list 2 2 2) u3) ;; Returns (some (2 2 2)) (as-max-len? (list 1 2 3) u2) ;; Returns none (as-max-len? \"hello\" u10) ;; Returns (some \"hello\") -(as-max-len? 0x010203 u10) ;; Returns (some 0x010203)" +(as-max-len? 0x010203 u10) ;; Returns (some 0x010203) +"# }; const LEN_API: SpecialAPI = SpecialAPI { input_type: "buff|list|string-ascii|string-utf8", output_type: "uint", signature: "(len sequence)", - description: "The `len` function returns the length of a given buffer, list or string.", - example: " -(len \"blockstack\") ;; Returns u10 + description: "The `len` function returns the length of a given `buff`, `list`, `string-ascii` or `string-utf8`.", + example: r#" +(len "blockstack") ;; Returns u10 (len (list 1 2 3 4 5)) ;; Returns u5 (len 0x010203) ;; Returns u3 -", +"#, }; const ELEMENT_AT_API: SpecialAPI = SpecialAPI { @@ -587,15 +590,16 @@ const ELEMENT_AT_API: SpecialAPI = SpecialAPI { output_type: "(optional (buff 1)|A|(string-ascii 1)|(string-utf8 1))", signature: "(element-at sequence index)", description: - "The `element-at` function returns the element at `index` in the provided sequence. + "The `element-at` function returns the element at `index` in the provided sequence (where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`). If `index` is greater than or equal to `(len sequence)`, this function returns `none`. For strings or buffers, this function will return 1-length strings or buffers.", - example: "(element-at \"blockstack\" u5) ;; Returns (some \"s\") + example: r#" +(element-at "blockstack" u5) ;; Returns (some "s") (element-at (list 1 2 3 4 5) u5) ;; Returns none (element-at (list 1 2 3 4 5) (+ u1 u2)) ;; Returns (some 4) -(element-at \"abcd\" u1) ;; Returns (some \"b\") +(element-at "abcd" u1) ;; Returns (some "b") (element-at 0xfb01 u1) ;; Returns (some 0x01) -", +"#, }; const INDEX_OF_API: SpecialAPI = SpecialAPI { @@ -606,17 +610,16 @@ string-utf8 (string-utf8 1)", output_type: "(optional uint)", signature: "(index-of sequence item)", description: "The `index-of` function returns the first index at which `item` can be -found, using `is-eq` checks, in the provided sequence, which is either -`buff`, `list`, `string-ascii` or `string-utf8`. - +found, using `is-eq` checks, in the provided sequence (where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`). If this item is not found in the sequence (or an empty string/buffer is supplied), this function returns `none`.", - example: "(index-of \"blockstack\" \"b\") ;; Returns (some u0) -(index-of \"blockstack\" \"k\") ;; Returns (some u4) -(index-of \"blockstack\" \"\") ;; Returns none + example: r#" +(index-of "blockstack" "b") ;; Returns (some u0) +(index-of "blockstack" "k") ;; Returns (some u4) +(index-of "blockstack" "") ;; Returns none (index-of (list 1 2 3 4 5) 6) ;; Returns none (index-of 0xfb01 0x01) ;; Returns (some u1) -", +"#, }; const LIST_API: SpecialAPI = SpecialAPI { From 9913e4a3ac6bf9dfb4f060a1925a79c2fa6b55cb Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Fri, 4 Jun 2021 12:52:46 -0500 Subject: [PATCH 07/20] Addressed comments. --- src/vm/docs/mod.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index 9c74e188f3..b9e262d558 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -484,10 +484,10 @@ no matter what kind of sequences the inputs are, the output is always a list.", }; const FILTER_API: SpecialAPI = SpecialAPI { - input_type: "Function((buff 1)) -> bool, buff + input_type: "Function(buff 1) -> bool, buff Function(A) -> bool, (list A) -Function((string-ascii 1)) -> bool, string-ascii -Function((string-utf8 1)) -> bool, string-utf8", +Function(string-ascii 1) -> bool, string-ascii +Function(string-utf8 1) -> bool, string-utf8", output_type: "buff (list A) string-ascii @@ -513,7 +513,8 @@ Function((string-utf8 1), B) -> B, string-utf8, B", output_type: "B", signature: "(fold func sequence initial-value)", description: "The `fold` function applies the input function `func` to each element of the -input sequence (where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`) _and_ the output of the previous application of the `fold` function. When invoked on +input sequence (where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`) +_and_ the output of the previous application of the `fold` function. When invoked on the first list element, it uses the `initial-value` as the second input. `fold` returns the last value returned by the successive applications. Note that the first argument is not evaluated thus has to be a literal function name.", @@ -538,7 +539,8 @@ string-ascii, string-ascii string-utf8, string-utf8", output_type: "buff|list|string-ascii|string-utf8", signature: "(concat sequence-a sequence-b)", - description: "The `concat` function takes two sequences (where each sequence is either `buff`, `list`, `string-ascii` or `string-utf8`) with the same entry type, + description: "The `concat` function takes two sequences (where each sequence is either +`buff`, `list`, `string-ascii` or `string-utf8`) with the same entry type, and returns a concatenated sequence of the same type, with seq_len = seq_len_a + seq_len_b.", example: r#" (concat "hello " "world") ;; Returns "hello world" @@ -590,7 +592,8 @@ const ELEMENT_AT_API: SpecialAPI = SpecialAPI { output_type: "(optional (buff 1)|A|(string-ascii 1)|(string-utf8 1))", signature: "(element-at sequence index)", description: - "The `element-at` function returns the element at `index` in the provided sequence (where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`). + "The `element-at` function returns the element at `index` in the provided sequence +(where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`). If `index` is greater than or equal to `(len sequence)`, this function returns `none`. For strings or buffers, this function will return 1-length strings or buffers.", example: r#" @@ -610,8 +613,9 @@ string-utf8 (string-utf8 1)", output_type: "(optional uint)", signature: "(index-of sequence item)", description: "The `index-of` function returns the first index at which `item` can be -found, using `is-eq` checks, in the provided sequence (where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`). -If this item is not found in the sequence (or an empty string/buffer is supplied), this +found, using `is-eq` checks, in the provided sequence (where a sequence is +either `buff`, `list`, `string-ascii` or `string-utf8`). If this item is not +found in the sequence (or an empty string/buffer is supplied), this function returns `none`.", example: r#" (index-of "blockstack" "b") ;; Returns (some u0) From 3f6e90ab5cb6934148630594f44bf04a5d086fad Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Fri, 4 Jun 2021 13:16:55 -0500 Subject: [PATCH 08/20] Addressed failing tests --- src/vm/docs/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index b9e262d558..c3b5f64284 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -545,7 +545,7 @@ and returns a concatenated sequence of the same type, with seq_len = seq_len_a + example: r#" (concat "hello " "world") ;; Returns "hello world" (concat 0x0102 0x0304) ;; Returns 0x01020304 -"# +"#, }; const APPEND_API: SpecialAPI = SpecialAPI { @@ -570,7 +570,7 @@ or equal to the supplied max-len, it returns `(some )`, otherwise it r example: r#" (as-max-len? (list 2 2 2) u3) ;; Returns (some (2 2 2)) (as-max-len? (list 1 2 3) u2) ;; Returns none -(as-max-len? \"hello\" u10) ;; Returns (some \"hello\") +(as-max-len? "hello" u10) ;; Returns (some "hello") (as-max-len? 0x010203 u10) ;; Returns (some 0x010203) "# }; @@ -591,8 +591,7 @@ const ELEMENT_AT_API: SpecialAPI = SpecialAPI { input_type: "buff|list|string-ascii|string-utf8 A, uint", output_type: "(optional (buff 1)|A|(string-ascii 1)|(string-utf8 1))", signature: "(element-at sequence index)", - description: - "The `element-at` function returns the element at `index` in the provided sequence + description: "The `element-at` function returns the element at `index` in the provided sequence (where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`). If `index` is greater than or equal to `(len sequence)`, this function returns `none`. For strings or buffers, this function will return 1-length strings or buffers.", From 5063aaf1ad6ef1c0e9a5b73b89ceafaf95faf2ba Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Mon, 7 Jun 2021 12:11:29 -0500 Subject: [PATCH 09/20] Use a virtual type 'sequence' to condense complicated disjoined types Instead of having long lists of disjoined types, e.g., buff|(list A)|string-ascii|string-utf8 We now condense these to being a 'virtual' type called 'sequence', which doesn't actually exist in the Clarity type hierarchy, but is used in the docs, and which is explained in each description in which it is used. --- src/vm/docs/mod.rs | 114 +++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 62 deletions(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index c3b5f64284..7a6312c2ee 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -463,16 +463,14 @@ inputted value.", }; const MAP_API: SpecialAPI = SpecialAPI { - input_type: "Function((buff 1)_1, ..., (buff 1)_N) -> X, (buff m)_1, ..., (buff m)_N -Function(A, B, ..., N) -> X, (list A1 A2 ... Am), (list B1 B2 ... Bm), ..., (list N1 N2 ... Nm) -Function((string-ascii 1)_1, ..., (string-ascii 1)_N) -> X, (string-ascii m)_1, ..., (string-ascii m)_N -Function((string-utf8 1)_1, ..., (string-utf8 1)_N) -> X, (string-utf8 m)_1, ..., (string-utf8 m)_N", - output_type: "(list X)", - signature: "(map func sequence-A sequence-B ... sequence-N)", - description: "The `map` function applies the input function `func` to each element of the -input sequences (where each sequence is either `buff`, `list`, `string-ascii` or `string-utf8`), and outputs a -_list_ of the same type containing the _outputs_ from those function applications. Note that, -no matter what kind of sequences the inputs are, the output is always a list.", + input_type: "Function(item_A, item_B, ..., item_N) -> X, sequence_A, sequence_B, ..., sequence_N", + output_type: "sequence_X", + signature: "(map func sequence_A sequence_B ... sequence_N)", + description: "The `map` function applies the function `func` to each element of the input sequences, +and outputs a _list_ of the same type containing the _outputs_ from those function applications. +Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, +for which the corresponding item element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +Note that, no matter what kind of sequences the inputs are, the output is always a list.", example: r#" (map not (list true false true false)) ;; Returns (false true false true) (map + (list 1 2 3) (list 1 2 3) (list 1 2 3)) ;; Returns (3 6 9) @@ -484,18 +482,14 @@ no matter what kind of sequences the inputs are, the output is always a list.", }; const FILTER_API: SpecialAPI = SpecialAPI { - input_type: "Function(buff 1) -> bool, buff -Function(A) -> bool, (list A) -Function(string-ascii 1) -> bool, string-ascii -Function(string-utf8 1) -> bool, string-utf8", - output_type: "buff -(list A) -string-ascii -string-utf8", + input_type: "Function(item_A) -> bool, sequence_A", + output_type: "sequence_A", signature: "(filter func sequence)", description: "The `filter` function applies the input function `func` to each element of the -input sequence (where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`), and returns the -same list with any elements removed for which the `func` returned `false`.", +input sequence, and returns the same sequence with any elements removed for which the `func` returned `false`. +Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, +for which the corresponding item element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +", example: r#" (filter not (list true false true false)) ;; Returns (false false) (define-private (is-a (char (string-utf8 1))) (is-eq char u"a")) @@ -506,16 +500,15 @@ same list with any elements removed for which the `func` returned `false`.", }; const FOLD_API: SpecialAPI = SpecialAPI { - input_type: "Function((buff 1), B) -> B, buff, B -Function(A, B) -> B, (list A), B -Function((string-ascii 1), B) -> B, string-ascii, B -Function((string-utf8 1), B) -> B, string-utf8, B", + input_type: "Function(item_A, B) -> B, sequence_A, B", output_type: "B", - signature: "(fold func sequence initial-value)", + signature: "(fold func sequence_A initial_value)", description: "The `fold` function applies the input function `func` to each element of the -input sequence (where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`) -_and_ the output of the previous application of the `fold` function. When invoked on -the first list element, it uses the `initial-value` as the second input. `fold` returns the last +input sequence _and_ the output of the previous application of the `fold` function. +Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, +for which the corresponding item element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +When invoked on +the first list element, it uses the `initial_value` as the second input. `fold` returns the last value returned by the successive applications. Note that the first argument is not evaluated thus has to be a literal function name.", example: r#" @@ -532,16 +525,13 @@ has to be a literal function name.", }; const CONCAT_API: SpecialAPI = SpecialAPI { - input_type: " -buff, buff -list, list -string-ascii, string-ascii -string-utf8, string-utf8", - output_type: "buff|list|string-ascii|string-utf8", - signature: "(concat sequence-a sequence-b)", - description: "The `concat` function takes two sequences (where each sequence is either -`buff`, `list`, `string-ascii` or `string-utf8`) with the same entry type, -and returns a concatenated sequence of the same type, with seq_len = seq_len_a + seq_len_b.", + input_type: "sequence_A, sequence_A", + output_type: "sequence_A", + signature: "(concat sequence_A sequence_B)", + description: "The `concat` function takes two sequences of the same type, +and returns a concatenated sequence of the same type, with seq_len = seq_len_a + seq_len_b. +Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`. +", example: r#" (concat "hello " "world") ;; Returns "hello world" (concat 0x0102 0x0304) ;; Returns 0x01020304 @@ -558,15 +548,14 @@ and outputs a list of the same type with max_len += 1.", }; const ASSERTS_MAX_LEN_API: SpecialAPI = SpecialAPI { - input_type: "buff|list|string-ascii|string-utf8, uint", - output_type: "(optional buff|list|string-ascii|string-utf8)", + input_type: "sequence_A, uint", + output_type: "sequence_A", signature: "(as-max-len? sequence u10)", - description: "The `as-max-len?` function takes a length N (must be a literal) and a sequence argument -(where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`), which must be typed as a list -or buffer of length M and outputs that same list or buffer, but typed with max length N. - -This function returns an optional type with the resulting sequence. If the input sequence is less than -or equal to the supplied max-len, it returns `(some )`, otherwise it returns `none`.", + description: "The `as-max-len?` function takes a length N (must be a literal) and a sequence argument. +This function returns an optional type. If the input sequence is less than +or equal to the supplied max-len, it returns `(some sequence_A)`, otherwise it returns `none`. +Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`. +", example: r#" (as-max-len? (list 2 2 2) u3) ;; Returns (some (2 2 2)) (as-max-len? (list 1 2 3) u2) ;; Returns none @@ -576,10 +565,12 @@ or equal to the supplied max-len, it returns `(some )`, otherwise it r }; const LEN_API: SpecialAPI = SpecialAPI { - input_type: "buff|list|string-ascii|string-utf8", + input_type: "sequence", output_type: "uint", signature: "(len sequence)", - description: "The `len` function returns the length of a given `buff`, `list`, `string-ascii` or `string-utf8`.", + description: "The `len` function returns the length of a given sequence. +Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`. + ", example: r#" (len "blockstack") ;; Returns u10 (len (list 1 2 3 4 5)) ;; Returns u5 @@ -588,13 +579,13 @@ const LEN_API: SpecialAPI = SpecialAPI { }; const ELEMENT_AT_API: SpecialAPI = SpecialAPI { - input_type: "buff|list|string-ascii|string-utf8 A, uint", - output_type: "(optional (buff 1)|A|(string-ascii 1)|(string-utf8 1))", + input_type: "sequence_A, uint", + output_type: "item_A", signature: "(element-at sequence index)", - description: "The `element-at` function returns the element at `index` in the provided sequence -(where a sequence is either `buff`, `list`, `string-ascii` or `string-utf8`). -If `index` is greater than or equal to `(len sequence)`, this function returns `none`. -For strings or buffers, this function will return 1-length strings or buffers.", + description: "The `element-at` function returns the element at `index` in the provided sequence. +Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, +for which the corresponding item element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +", example: r#" (element-at "blockstack" u5) ;; Returns (some "s") (element-at (list 1 2 3 4 5) u5) ;; Returns none @@ -605,17 +596,16 @@ For strings or buffers, this function will return 1-length strings or buffers.", }; const INDEX_OF_API: SpecialAPI = SpecialAPI { - input_type: "buff, (buff 1) -list A, A -string-ascii (string-ascii 1) -string-utf8 (string-utf8 1)", + input_type: "sequence_A, item_A", output_type: "(optional uint)", - signature: "(index-of sequence item)", + signature: "(index-of sequence_A item_A)", description: "The `index-of` function returns the first index at which `item` can be -found, using `is-eq` checks, in the provided sequence (where a sequence is -either `buff`, `list`, `string-ascii` or `string-utf8`). If this item is not +found, using `is-eq` checks, in the provided sequence. If this item is not found in the sequence (or an empty string/buffer is supplied), this -function returns `none`.", +function returns `none`. +Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, +for which the corresponding item element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +", example: r#" (index-of "blockstack" "b") ;; Returns (some u0) (index-of "blockstack" "k") ;; Returns (some u4) From 6a81b0dfad650eed52eefe778bbf217dec3cf134 Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Mon, 7 Jun 2021 12:14:33 -0500 Subject: [PATCH 10/20] Ran 'cargo fmt' --- src/vm/docs/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index 7a6312c2ee..48bc789cf0 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -551,7 +551,8 @@ const ASSERTS_MAX_LEN_API: SpecialAPI = SpecialAPI { input_type: "sequence_A, uint", output_type: "sequence_A", signature: "(as-max-len? sequence u10)", - description: "The `as-max-len?` function takes a length N (must be a literal) and a sequence argument. + description: + "The `as-max-len?` function takes a length N (must be a literal) and a sequence argument. This function returns an optional type. If the input sequence is less than or equal to the supplied max-len, it returns `(some sequence_A)`, otherwise it returns `none`. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`. @@ -561,7 +562,7 @@ Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf (as-max-len? (list 1 2 3) u2) ;; Returns none (as-max-len? "hello" u10) ;; Returns (some "hello") (as-max-len? 0x010203 u10) ;; Returns (some 0x010203) -"# +"#, }; const LEN_API: SpecialAPI = SpecialAPI { From 96e9e754bccf3ff69ad0f87bed2fc729525deae5 Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Mon, 7 Jun 2021 12:19:38 -0500 Subject: [PATCH 11/20] Fixed some signatures Some signatures were inconsistent with the input/output types. --- src/vm/docs/mod.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index 48bc789cf0..ea8f6c08cd 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -484,7 +484,7 @@ Note that, no matter what kind of sequences the inputs are, the output is always const FILTER_API: SpecialAPI = SpecialAPI { input_type: "Function(item_A) -> bool, sequence_A", output_type: "sequence_A", - signature: "(filter func sequence)", + signature: "(filter func sequence_A)", description: "The `filter` function applies the input function `func` to each element of the input sequence, and returns the same sequence with any elements removed for which the `func` returned `false`. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, @@ -525,11 +525,12 @@ has to be a literal function name.", }; const CONCAT_API: SpecialAPI = SpecialAPI { - input_type: "sequence_A, sequence_A", + input_type: "sequence1_A, sequence2_A", output_type: "sequence_A", - signature: "(concat sequence_A sequence_B)", + signature: "(concat sequence1_A sequence2_A)", description: "The `concat` function takes two sequences of the same type, -and returns a concatenated sequence of the same type, with seq_len = seq_len_a + seq_len_b. +and returns a concatenated sequence of the same type, with the resulting +seq_len = seq1_len + seq2_len. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`. ", example: r#" @@ -550,7 +551,7 @@ and outputs a list of the same type with max_len += 1.", const ASSERTS_MAX_LEN_API: SpecialAPI = SpecialAPI { input_type: "sequence_A, uint", output_type: "sequence_A", - signature: "(as-max-len? sequence u10)", + signature: "(as-max-len? sequence_A u10)", description: "The `as-max-len?` function takes a length N (must be a literal) and a sequence argument. This function returns an optional type. If the input sequence is less than @@ -582,7 +583,7 @@ Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf const ELEMENT_AT_API: SpecialAPI = SpecialAPI { input_type: "sequence_A, uint", output_type: "item_A", - signature: "(element-at sequence index)", + signature: "(element-at sequence_A index)", description: "The `element-at` function returns the element at `index` in the provided sequence. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, for which the corresponding item element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. From f1a254601dbff2cf444d04e9f1502c3768136902 Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Mon, 7 Jun 2021 12:23:15 -0500 Subject: [PATCH 12/20] Fixed one more signature This changes a signature that antedates this PR to be more consistent. --- src/vm/docs/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index ea8f6c08cd..db11c672ab 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -551,7 +551,7 @@ and outputs a list of the same type with max_len += 1.", const ASSERTS_MAX_LEN_API: SpecialAPI = SpecialAPI { input_type: "sequence_A, uint", output_type: "sequence_A", - signature: "(as-max-len? sequence_A u10)", + signature: "(as-max-len? sequence_A size)", description: "The `as-max-len?` function takes a length N (must be a literal) and a sequence argument. This function returns an optional type. If the input sequence is less than From 296cd724000244bfa0763464d13a7e79b11ccc1e Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Mon, 7 Jun 2021 13:19:10 -0500 Subject: [PATCH 13/20] Re-worked the types and signatures Various changes to the focus functions were made for clarity and consistency. --- src/vm/docs/mod.rs | 79 ++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index db11c672ab..06adee54e7 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -463,14 +463,15 @@ inputted value.", }; const MAP_API: SpecialAPI = SpecialAPI { - input_type: "Function(item_A, item_B, ..., item_N) -> X, sequence_A, sequence_B, ..., sequence_N", + input_type: "Function(A, B, ..., N) -> X, sequence_A, sequence_B, ..., sequence_N", output_type: "sequence_X", signature: "(map func sequence_A sequence_B ... sequence_N)", - description: "The `map` function applies the function `func` to each element of the input sequences, -and outputs a _list_ of the same type containing the _outputs_ from those function applications. + description: "The `map` function applies the function `func` to each corresponding element of the input sequences, +and outputs a _list_ of the same type containing the outputs from those function applications. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, -for which the corresponding item element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. -Note that, no matter what kind of sequences the inputs are, the output is always a list.", +for which the corresponding element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +The `func` argument must be a literal function name. +Also, note that, no matter what kind of sequences the inputs are, the output is always a list.", example: r#" (map not (list true false true false)) ;; Returns (false true false true) (map + (list 1 2 3) (list 1 2 3) (list 1 2 3)) ;; Returns (3 6 9) @@ -482,13 +483,14 @@ Note that, no matter what kind of sequences the inputs are, the output is always }; const FILTER_API: SpecialAPI = SpecialAPI { - input_type: "Function(item_A) -> bool, sequence_A", + input_type: "Function(A) -> bool, sequence_A", output_type: "sequence_A", - signature: "(filter func sequence_A)", + signature: "(filter func sequence)", description: "The `filter` function applies the input function `func` to each element of the -input sequence, and returns the same sequence with any elements removed for which the `func` returned `false`. +input sequence, and returns the same sequence with any elements removed for which `func` returned `false`. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, -for which the corresponding item element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +for which the corresponding element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +The `func` argument must be a literal function name. ", example: r#" (filter not (list true false true false)) ;; Returns (false false) @@ -500,17 +502,23 @@ for which the corresponding item element types are, repsectively, `A`, `(buff 1) }; const FOLD_API: SpecialAPI = SpecialAPI { - input_type: "Function(item_A, B) -> B, sequence_A, B", + input_type: "Function(A, B) -> B, sequence_A, B", output_type: "B", - signature: "(fold func sequence_A initial_value)", - description: "The `fold` function applies the input function `func` to each element of the -input sequence _and_ the output of the previous application of the `fold` function. + signature: "(fold func sequence_A initial_B)", + description: "The `fold` function condenses `sequence_A` into a value of type +`B` by recursively applies the function `func` to each element of the +input sequence _and_ the output of a previous application of `func`. + +`fold` uses `initial_B` in the initial application of `func`, along with the +first element of `sequence_A`. The resulting value of type `B` is used for the +next application of `func`, along with the next element of `sequence_A` and so +on. `fold` returns the last value of type `B` returned by these successive +applications `func`. + Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, -for which the corresponding item element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. -When invoked on -the first list element, it uses the `initial_value` as the second input. `fold` returns the last -value returned by the successive applications. Note that the first argument is not evaluated thus -has to be a literal function name.", +for which the corresponding element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +The `func` argument must be a literal function name. +", example: r#" (fold * (list 2 2 2) 1) ;; Returns 8 (fold * (list 2 2 2) 0) ;; Returns 0 @@ -525,15 +533,16 @@ has to be a literal function name.", }; const CONCAT_API: SpecialAPI = SpecialAPI { - input_type: "sequence1_A, sequence2_A", + input_type: "sequence_A, sequence_A", output_type: "sequence_A", - signature: "(concat sequence1_A sequence2_A)", + signature: "(concat sequence1 sequence2)", description: "The `concat` function takes two sequences of the same type, and returns a concatenated sequence of the same type, with the resulting -seq_len = seq1_len + seq2_len. +sequence_len = sequence1_len + sequence2_len. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`. ", example: r#" +(concat (list 1 2) (list 3 4)) ;; Returns (list 1 2 3 4) (concat "hello " "world") ;; Returns "hello world" (concat 0x0102 0x0304) ;; Returns 0x01020304 "#, @@ -551,11 +560,11 @@ and outputs a list of the same type with max_len += 1.", const ASSERTS_MAX_LEN_API: SpecialAPI = SpecialAPI { input_type: "sequence_A, uint", output_type: "sequence_A", - signature: "(as-max-len? sequence_A size)", + signature: "(as-max-len? sequence max_length)", description: - "The `as-max-len?` function takes a length N (must be a literal) and a sequence argument. -This function returns an optional type. If the input sequence is less than -or equal to the supplied max-len, it returns `(some sequence_A)`, otherwise it returns `none`. + "The `as-max-len?` function takes a sequence argument and a uint-valued, literal length argument. +The function returns an optional type. If the input sequence length is less than +or equal to the supplied max_length, this returns `(some sequence)`, otherwise it returns `none`. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`. ", example: r#" @@ -567,7 +576,7 @@ Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf }; const LEN_API: SpecialAPI = SpecialAPI { - input_type: "sequence", + input_type: "sequence_A", output_type: "uint", signature: "(len sequence)", description: "The `len` function returns the length of a given sequence. @@ -582,11 +591,11 @@ Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf const ELEMENT_AT_API: SpecialAPI = SpecialAPI { input_type: "sequence_A, uint", - output_type: "item_A", - signature: "(element-at sequence_A index)", + output_type: "A", + signature: "(element-at sequence index)", description: "The `element-at` function returns the element at `index` in the provided sequence. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, -for which the corresponding item element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +for which the corresponding element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. ", example: r#" (element-at "blockstack" u5) ;; Returns (some "s") @@ -598,15 +607,15 @@ for which the corresponding item element types are, repsectively, `A`, `(buff 1) }; const INDEX_OF_API: SpecialAPI = SpecialAPI { - input_type: "sequence_A, item_A", + input_type: "sequence_A, A", output_type: "(optional uint)", - signature: "(index-of sequence_A item_A)", + signature: "(index-of sequence item)", description: "The `index-of` function returns the first index at which `item` can be -found, using `is-eq` checks, in the provided sequence. If this item is not -found in the sequence (or an empty string/buffer is supplied), this -function returns `none`. +found, using `is-eq` checks, in the provided sequence. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, -for which the corresponding item element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +for which the corresponding element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +If the target item is not found in the sequence (or if an empty string or buffer is +supplied), this function returns `none`. ", example: r#" (index-of "blockstack" "b") ;; Returns (some u0) From f490959d78d056c7c3218b7745c5c675fc23f28c Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Tue, 8 Jun 2021 17:21:06 -0500 Subject: [PATCH 14/20] Addressing comments by @pavitthrap --- src/vm/docs/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index 06adee54e7..9b95dedba4 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -464,7 +464,7 @@ inputted value.", const MAP_API: SpecialAPI = SpecialAPI { input_type: "Function(A, B, ..., N) -> X, sequence_A, sequence_B, ..., sequence_N", - output_type: "sequence_X", + output_type: "(list X)", signature: "(map func sequence_A sequence_B ... sequence_N)", description: "The `map` function applies the function `func` to each corresponding element of the input sequences, and outputs a _list_ of the same type containing the outputs from those function applications. @@ -591,7 +591,7 @@ Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf const ELEMENT_AT_API: SpecialAPI = SpecialAPI { input_type: "sequence_A, uint", - output_type: "A", + output_type: "(optional A)", signature: "(element-at sequence index)", description: "The `element-at` function returns the element at `index` in the provided sequence. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, From 4984611c1e2e8344b5726d26b43712762679e887 Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Mon, 14 Jun 2021 11:38:54 -0500 Subject: [PATCH 15/20] Fixed a vm:docs test case --- src/vm/docs/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index 9b95dedba4..007cd767f7 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -542,7 +542,7 @@ sequence_len = sequence1_len + sequence2_len. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`. ", example: r#" -(concat (list 1 2) (list 3 4)) ;; Returns (list 1 2 3 4) +(concat (list 1 2) (list 3 4)) ;; Returns (1 2 3 4) (concat "hello " "world") ;; Returns "hello world" (concat 0x0102 0x0304) ;; Returns 0x01020304 "#, From f24da4b8c5398028d0b05b4f642ba6a180e6b087 Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Mon, 14 Jun 2021 11:46:36 -0500 Subject: [PATCH 16/20] Fixed some typos --- src/vm/docs/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index 007cd767f7..94e616428e 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -469,7 +469,7 @@ const MAP_API: SpecialAPI = SpecialAPI { description: "The `map` function applies the function `func` to each corresponding element of the input sequences, and outputs a _list_ of the same type containing the outputs from those function applications. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, -for which the corresponding element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +for which the corresponding element types are, respectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. The `func` argument must be a literal function name. Also, note that, no matter what kind of sequences the inputs are, the output is always a list.", example: r#" @@ -489,7 +489,7 @@ const FILTER_API: SpecialAPI = SpecialAPI { description: "The `filter` function applies the input function `func` to each element of the input sequence, and returns the same sequence with any elements removed for which `func` returned `false`. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, -for which the corresponding element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +for which the corresponding element types are, respectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. The `func` argument must be a literal function name. ", example: r#" @@ -516,7 +516,7 @@ on. `fold` returns the last value of type `B` returned by these successive applications `func`. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, -for which the corresponding element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +for which the corresponding element types are, respectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. The `func` argument must be a literal function name. ", example: r#" @@ -595,7 +595,7 @@ const ELEMENT_AT_API: SpecialAPI = SpecialAPI { signature: "(element-at sequence index)", description: "The `element-at` function returns the element at `index` in the provided sequence. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, -for which the corresponding element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +for which the corresponding element types are, respectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. ", example: r#" (element-at "blockstack" u5) ;; Returns (some "s") @@ -613,7 +613,7 @@ const INDEX_OF_API: SpecialAPI = SpecialAPI { description: "The `index-of` function returns the first index at which `item` can be found, using `is-eq` checks, in the provided sequence. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, -for which the corresponding element types are, repsectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. +for which the corresponding element types are, respectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. If the target item is not found in the sequence (or if an empty string or buffer is supplied), this function returns `none`. ", From 662fca91fb8d719adc2732a2da36044c96352fef Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Mon, 14 Jun 2021 11:48:42 -0500 Subject: [PATCH 17/20] removed some files that aren't in 'develop' --- CHANGELOG.md | 6 -- src/vm/contexts.rs | 12 --- src/vm/coverage.rs | 224 --------------------------------------------- src/vm/mod.rs | 14 --- 4 files changed, 256 deletions(-) delete mode 100644 src/vm/coverage.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 79cc6a54f5..4e036e048a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,6 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to the versioning scheme outlined in the [README.md](README.md). -## [Unreleased] - -## Added - -- lcov compatible coverage reporting in `clarity-cli` for Clarity contract testing. - ## [2.0.11.1.0] This software update is our monthly release. It introduces fixes and features for both developers and miners. diff --git a/src/vm/contexts.rs b/src/vm/contexts.rs index f91673059b..e047430aed 100644 --- a/src/vm/contexts.rs +++ b/src/vm/contexts.rs @@ -52,8 +52,6 @@ use crate::types::chainstate::StacksMicroblockHeader; use serde::Serialize; use vm::costs::cost_functions::ClarityCostFunction; -use vm::coverage::CoverageReporter; - pub const MAX_CONTEXT_DEPTH: u16 = 256; // TODO: @@ -193,7 +191,6 @@ pub struct GlobalContext<'a> { read_only: Vec, pub cost_track: LimitedCostTracker, pub mainnet: bool, - pub coverage_reporting: Option, } #[derive(Serialize, Deserialize, Clone)] @@ -548,14 +545,6 @@ impl<'a> OwnedEnvironment<'a> { } } - pub fn set_coverage_reporter(&mut self, reporter: CoverageReporter) { - self.context.coverage_reporting = Some(reporter) - } - - pub fn take_coverage_reporter(&mut self) -> Option { - self.context.coverage_reporting.take() - } - pub fn new_free(mainnet: bool, database: ClarityDatabase<'a>) -> OwnedEnvironment<'a> { OwnedEnvironment { context: GlobalContext::new(mainnet, database, LimitedCostTracker::new_free()), @@ -1360,7 +1349,6 @@ impl<'a> GlobalContext<'a> { asset_maps: Vec::new(), event_batches: Vec::new(), mainnet, - coverage_reporting: None, } } diff --git a/src/vm/coverage.rs b/src/vm/coverage.rs deleted file mode 100644 index 9a532852d9..0000000000 --- a/src/vm/coverage.rs +++ /dev/null @@ -1,224 +0,0 @@ -use std::{ - collections::{BTreeMap, HashMap, HashSet}, - fs::File, - io::Write, -}; - -use serde_json::Value as JsonValue; -use vm::types::QualifiedContractIdentifier; -use vm::SymbolicExpression; - -use super::functions::define::DefineFunctionsParsed; - -pub struct CoverageReporter { - executed_lines: HashMap>, -} - -#[derive(Serialize, Deserialize)] -struct ContractFileInfo { - contract: String, - src_file: String, - executable_lines: Vec, -} - -#[derive(Serialize, Deserialize)] -struct CoverageFileInfo { - coverage: HashMap>, -} - -impl CoverageReporter { - pub fn new() -> CoverageReporter { - CoverageReporter { - executed_lines: HashMap::new(), - } - } - - #[cfg(not(feature = "developer-mode"))] - pub fn report_eval( - &mut self, - _expr: &SymbolicExpression, - _contract: &QualifiedContractIdentifier, - ) { - } - - #[cfg(feature = "developer-mode")] - pub fn report_eval( - &mut self, - expr: &SymbolicExpression, - contract: &QualifiedContractIdentifier, - ) { - if expr.match_list().is_some() { - // don't count the whole list expression: wait until we've eval'ed the - // list components - return; - } - - // other sexps can only span 1 line - let line_executed = expr.span.start_line; - - if let Some(execution_map_contract) = self.executed_lines.get_mut(contract) { - if let Some(execution_count) = execution_map_contract.get_mut(&line_executed) { - *execution_count += 1; - } else { - execution_map_contract.insert(line_executed, 1); - } - } else { - let mut execution_map_contract = HashMap::new(); - execution_map_contract.insert(line_executed, 1); - self.executed_lines - .insert(contract.clone(), execution_map_contract); - } - } - - pub fn to_file + Copy>(&self, filename: P) -> std::io::Result<()> { - let f = File::create(filename)?; - let mut coverage = HashMap::new(); - for (contract, execution_map) in self.executed_lines.iter() { - let mut executed_lines = vec![]; - for (line, count) in execution_map.iter() { - executed_lines.push((*line, *count)); - } - executed_lines.sort_by_key(|f| f.0); - - coverage.insert(contract.to_string(), executed_lines); - } - - let out = CoverageFileInfo { coverage }; - if let Err(e) = serde_json::to_writer(f, &out) { - error!( - "Failed to serialize JSON to coverage file {}: {}", - filename.as_ref().display(), - e - ); - return Err(e.into()); - } - - Ok(()) - } - - fn executable_lines(exprs: &[SymbolicExpression]) -> Vec { - let mut lines = vec![]; - let mut lines_seen = HashSet::new(); - for expression in exprs.iter() { - let mut frontier = vec![expression]; - while let Some(cur_expr) = frontier.pop() { - // handle defines: the `define-` atom is non executable, and neither are any of the type arguments, - // but the bodies of functions, the value of a constant, initial values for variables, and the - // max supply of FTs - if let Some(define_expr) = DefineFunctionsParsed::try_parse(cur_expr).ok().flatten() - { - match define_expr { - DefineFunctionsParsed::Constant { name: _, value } => { - frontier.push(value); - } - DefineFunctionsParsed::PrivateFunction { signature: _, body } - | DefineFunctionsParsed::PublicFunction { signature: _, body } - | DefineFunctionsParsed::ReadOnlyFunction { signature: _, body } => { - frontier.push(body); - } - DefineFunctionsParsed::BoundedFungibleToken { - name: _, - max_supply, - } => { - frontier.push(max_supply); - } - DefineFunctionsParsed::PersistedVariable { - name: _, - data_type: _, - initial, - } => { - frontier.push(initial); - } - DefineFunctionsParsed::NonFungibleToken { .. } => {} - DefineFunctionsParsed::UnboundedFungibleToken { .. } => {} - DefineFunctionsParsed::Map { .. } => {} - DefineFunctionsParsed::Trait { .. } => {} - DefineFunctionsParsed::UseTrait { .. } => {} - DefineFunctionsParsed::ImplTrait { .. } => {} - } - - continue; - } - - if let Some(children) = cur_expr.match_list() { - // don't count list expressions as a whole, just their children - frontier.extend(children); - } else { - let line = cur_expr.span.start_line; - if !lines_seen.contains(&line) { - lines_seen.insert(line); - lines.push(line); - } - } - } - } - - lines.sort(); - lines - } - - pub fn register_src_file + Copy>( - contract: &QualifiedContractIdentifier, - src_file_name: &str, - ast: &[SymbolicExpression], - filename: P, - ) -> std::io::Result<()> { - let f = File::create(filename)?; - - let executable_lines = CoverageReporter::executable_lines(ast); - - let json = ContractFileInfo { - contract: contract.to_string(), - src_file: src_file_name.to_string(), - executable_lines, - }; - - if let Err(e) = serde_json::to_writer(f, &json) { - error!( - "Failed to serialize JSON to coverage file {}: {}", - filename.as_ref().display(), - e - ); - return Err(e.into()); - } - Ok(()) - } - - pub fn produce_lcov>( - out_filename: &str, - register_files: &[P], - coverage_files: &[P], - ) -> std::io::Result<()> { - let mut out = File::create(out_filename)?; - - for contract_filename in register_files.iter() { - let reader = File::open(contract_filename)?; - let info: ContractFileInfo = serde_json::from_reader(reader)?; - let mut summed_coverage = BTreeMap::new(); - for coverage_filename in coverage_files.iter() { - let cov_reader = File::open(coverage_filename)?; - let coverage: CoverageFileInfo = serde_json::from_reader(cov_reader)?; - if let Some(contract_coverage) = coverage.coverage.get(&info.contract) { - for (line, count) in contract_coverage.iter() { - if let Some(line_count) = summed_coverage.get_mut(line) { - *line_count += *count; - } else { - summed_coverage.insert(*line, *count); - } - } - } - } - writeln!(out, "TN:{}", &info.contract)?; - writeln!(out, "SF:{}", &info.src_file)?; - for line in info.executable_lines.iter() { - let count = summed_coverage.get(line).cloned().unwrap_or(0); - writeln!(out, "DA:{},{}", line, count)?; - } - writeln!(out, "LH:{}", summed_coverage.len())?; - writeln!(out, "LF:{}", &info.executable_lines.len())?; - writeln!(out, "end_of_record")?; - } - - Ok(()) - } -} diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 95ceb7f676..c067fc164d 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -38,8 +38,6 @@ mod variables; pub mod analysis; pub mod docs; -pub mod coverage; - #[cfg(test)] pub mod tests; @@ -208,10 +206,6 @@ pub fn eval<'a>( Atom, AtomValue, Field, List, LiteralValue, TraitReference, }; - if let Some(ref mut coverage_tracker) = env.global_context.coverage_reporting { - coverage_tracker.report_eval(exp, &env.contract_context.contract_identifier); - } - match exp.expr { AtomValue(ref value) | LiteralValue(ref value) => Ok(value.clone()), Atom(ref value) => lookup_variable(&value, context, env), @@ -219,14 +213,6 @@ pub fn eval<'a>( let (function_variable, rest) = children .split_first() .ok_or(CheckErrors::NonFunctionApplication)?; - - if let Some(ref mut coverage_tracker) = env.global_context.coverage_reporting { - coverage_tracker.report_eval( - &function_variable, - &env.contract_context.contract_identifier, - ); - } - let function_name = function_variable .match_atom() .ok_or(CheckErrors::BadFunctionName)?; From 1030b75364b79d1369ba63f3b92c5fcb4448e75c Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Mon, 14 Jun 2021 11:52:50 -0500 Subject: [PATCH 18/20] Trying to rever CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e036e048a..79cc6a54f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to the versioning scheme outlined in the [README.md](README.md). +## [Unreleased] + +## Added + +- lcov compatible coverage reporting in `clarity-cli` for Clarity contract testing. + ## [2.0.11.1.0] This software update is our monthly release. It introduces fixes and features for both developers and miners. From ab21f2a2cdfbd6258c7a4cdf1815661ba525221b Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Mon, 14 Jun 2021 11:53:50 -0500 Subject: [PATCH 19/20] Reverting files to the 'develop' branch versions --- src/vm/contexts.rs | 12 +++ src/vm/coverage.rs | 224 +++++++++++++++++++++++++++++++++++++++++++++ src/vm/mod.rs | 14 +++ 3 files changed, 250 insertions(+) create mode 100644 src/vm/coverage.rs diff --git a/src/vm/contexts.rs b/src/vm/contexts.rs index e047430aed..f91673059b 100644 --- a/src/vm/contexts.rs +++ b/src/vm/contexts.rs @@ -52,6 +52,8 @@ use crate::types::chainstate::StacksMicroblockHeader; use serde::Serialize; use vm::costs::cost_functions::ClarityCostFunction; +use vm::coverage::CoverageReporter; + pub const MAX_CONTEXT_DEPTH: u16 = 256; // TODO: @@ -191,6 +193,7 @@ pub struct GlobalContext<'a> { read_only: Vec, pub cost_track: LimitedCostTracker, pub mainnet: bool, + pub coverage_reporting: Option, } #[derive(Serialize, Deserialize, Clone)] @@ -545,6 +548,14 @@ impl<'a> OwnedEnvironment<'a> { } } + pub fn set_coverage_reporter(&mut self, reporter: CoverageReporter) { + self.context.coverage_reporting = Some(reporter) + } + + pub fn take_coverage_reporter(&mut self) -> Option { + self.context.coverage_reporting.take() + } + pub fn new_free(mainnet: bool, database: ClarityDatabase<'a>) -> OwnedEnvironment<'a> { OwnedEnvironment { context: GlobalContext::new(mainnet, database, LimitedCostTracker::new_free()), @@ -1349,6 +1360,7 @@ impl<'a> GlobalContext<'a> { asset_maps: Vec::new(), event_batches: Vec::new(), mainnet, + coverage_reporting: None, } } diff --git a/src/vm/coverage.rs b/src/vm/coverage.rs new file mode 100644 index 0000000000..9a532852d9 --- /dev/null +++ b/src/vm/coverage.rs @@ -0,0 +1,224 @@ +use std::{ + collections::{BTreeMap, HashMap, HashSet}, + fs::File, + io::Write, +}; + +use serde_json::Value as JsonValue; +use vm::types::QualifiedContractIdentifier; +use vm::SymbolicExpression; + +use super::functions::define::DefineFunctionsParsed; + +pub struct CoverageReporter { + executed_lines: HashMap>, +} + +#[derive(Serialize, Deserialize)] +struct ContractFileInfo { + contract: String, + src_file: String, + executable_lines: Vec, +} + +#[derive(Serialize, Deserialize)] +struct CoverageFileInfo { + coverage: HashMap>, +} + +impl CoverageReporter { + pub fn new() -> CoverageReporter { + CoverageReporter { + executed_lines: HashMap::new(), + } + } + + #[cfg(not(feature = "developer-mode"))] + pub fn report_eval( + &mut self, + _expr: &SymbolicExpression, + _contract: &QualifiedContractIdentifier, + ) { + } + + #[cfg(feature = "developer-mode")] + pub fn report_eval( + &mut self, + expr: &SymbolicExpression, + contract: &QualifiedContractIdentifier, + ) { + if expr.match_list().is_some() { + // don't count the whole list expression: wait until we've eval'ed the + // list components + return; + } + + // other sexps can only span 1 line + let line_executed = expr.span.start_line; + + if let Some(execution_map_contract) = self.executed_lines.get_mut(contract) { + if let Some(execution_count) = execution_map_contract.get_mut(&line_executed) { + *execution_count += 1; + } else { + execution_map_contract.insert(line_executed, 1); + } + } else { + let mut execution_map_contract = HashMap::new(); + execution_map_contract.insert(line_executed, 1); + self.executed_lines + .insert(contract.clone(), execution_map_contract); + } + } + + pub fn to_file + Copy>(&self, filename: P) -> std::io::Result<()> { + let f = File::create(filename)?; + let mut coverage = HashMap::new(); + for (contract, execution_map) in self.executed_lines.iter() { + let mut executed_lines = vec![]; + for (line, count) in execution_map.iter() { + executed_lines.push((*line, *count)); + } + executed_lines.sort_by_key(|f| f.0); + + coverage.insert(contract.to_string(), executed_lines); + } + + let out = CoverageFileInfo { coverage }; + if let Err(e) = serde_json::to_writer(f, &out) { + error!( + "Failed to serialize JSON to coverage file {}: {}", + filename.as_ref().display(), + e + ); + return Err(e.into()); + } + + Ok(()) + } + + fn executable_lines(exprs: &[SymbolicExpression]) -> Vec { + let mut lines = vec![]; + let mut lines_seen = HashSet::new(); + for expression in exprs.iter() { + let mut frontier = vec![expression]; + while let Some(cur_expr) = frontier.pop() { + // handle defines: the `define-` atom is non executable, and neither are any of the type arguments, + // but the bodies of functions, the value of a constant, initial values for variables, and the + // max supply of FTs + if let Some(define_expr) = DefineFunctionsParsed::try_parse(cur_expr).ok().flatten() + { + match define_expr { + DefineFunctionsParsed::Constant { name: _, value } => { + frontier.push(value); + } + DefineFunctionsParsed::PrivateFunction { signature: _, body } + | DefineFunctionsParsed::PublicFunction { signature: _, body } + | DefineFunctionsParsed::ReadOnlyFunction { signature: _, body } => { + frontier.push(body); + } + DefineFunctionsParsed::BoundedFungibleToken { + name: _, + max_supply, + } => { + frontier.push(max_supply); + } + DefineFunctionsParsed::PersistedVariable { + name: _, + data_type: _, + initial, + } => { + frontier.push(initial); + } + DefineFunctionsParsed::NonFungibleToken { .. } => {} + DefineFunctionsParsed::UnboundedFungibleToken { .. } => {} + DefineFunctionsParsed::Map { .. } => {} + DefineFunctionsParsed::Trait { .. } => {} + DefineFunctionsParsed::UseTrait { .. } => {} + DefineFunctionsParsed::ImplTrait { .. } => {} + } + + continue; + } + + if let Some(children) = cur_expr.match_list() { + // don't count list expressions as a whole, just their children + frontier.extend(children); + } else { + let line = cur_expr.span.start_line; + if !lines_seen.contains(&line) { + lines_seen.insert(line); + lines.push(line); + } + } + } + } + + lines.sort(); + lines + } + + pub fn register_src_file + Copy>( + contract: &QualifiedContractIdentifier, + src_file_name: &str, + ast: &[SymbolicExpression], + filename: P, + ) -> std::io::Result<()> { + let f = File::create(filename)?; + + let executable_lines = CoverageReporter::executable_lines(ast); + + let json = ContractFileInfo { + contract: contract.to_string(), + src_file: src_file_name.to_string(), + executable_lines, + }; + + if let Err(e) = serde_json::to_writer(f, &json) { + error!( + "Failed to serialize JSON to coverage file {}: {}", + filename.as_ref().display(), + e + ); + return Err(e.into()); + } + Ok(()) + } + + pub fn produce_lcov>( + out_filename: &str, + register_files: &[P], + coverage_files: &[P], + ) -> std::io::Result<()> { + let mut out = File::create(out_filename)?; + + for contract_filename in register_files.iter() { + let reader = File::open(contract_filename)?; + let info: ContractFileInfo = serde_json::from_reader(reader)?; + let mut summed_coverage = BTreeMap::new(); + for coverage_filename in coverage_files.iter() { + let cov_reader = File::open(coverage_filename)?; + let coverage: CoverageFileInfo = serde_json::from_reader(cov_reader)?; + if let Some(contract_coverage) = coverage.coverage.get(&info.contract) { + for (line, count) in contract_coverage.iter() { + if let Some(line_count) = summed_coverage.get_mut(line) { + *line_count += *count; + } else { + summed_coverage.insert(*line, *count); + } + } + } + } + writeln!(out, "TN:{}", &info.contract)?; + writeln!(out, "SF:{}", &info.src_file)?; + for line in info.executable_lines.iter() { + let count = summed_coverage.get(line).cloned().unwrap_or(0); + writeln!(out, "DA:{},{}", line, count)?; + } + writeln!(out, "LH:{}", summed_coverage.len())?; + writeln!(out, "LF:{}", &info.executable_lines.len())?; + writeln!(out, "end_of_record")?; + } + + Ok(()) + } +} diff --git a/src/vm/mod.rs b/src/vm/mod.rs index c067fc164d..95ceb7f676 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -38,6 +38,8 @@ mod variables; pub mod analysis; pub mod docs; +pub mod coverage; + #[cfg(test)] pub mod tests; @@ -206,6 +208,10 @@ pub fn eval<'a>( Atom, AtomValue, Field, List, LiteralValue, TraitReference, }; + if let Some(ref mut coverage_tracker) = env.global_context.coverage_reporting { + coverage_tracker.report_eval(exp, &env.contract_context.contract_identifier); + } + match exp.expr { AtomValue(ref value) | LiteralValue(ref value) => Ok(value.clone()), Atom(ref value) => lookup_variable(&value, context, env), @@ -213,6 +219,14 @@ pub fn eval<'a>( let (function_variable, rest) = children .split_first() .ok_or(CheckErrors::NonFunctionApplication)?; + + if let Some(ref mut coverage_tracker) = env.global_context.coverage_reporting { + coverage_tracker.report_eval( + &function_variable, + &env.contract_context.contract_identifier, + ); + } + let function_name = function_variable .match_atom() .ok_or(CheckErrors::BadFunctionName)?; From 105cf37db3e7619f7aa12205d8b84a270a6e650b Mon Sep 17 00:00:00 2001 From: Greg Coppola Date: Mon, 14 Jun 2021 16:23:52 -0500 Subject: [PATCH 20/20] Removed a trailing space character. --- src/vm/docs/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index 94e616428e..ef8a73ceb5 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -611,7 +611,7 @@ const INDEX_OF_API: SpecialAPI = SpecialAPI { output_type: "(optional uint)", signature: "(index-of sequence item)", description: "The `index-of` function returns the first index at which `item` can be -found, using `is-eq` checks, in the provided sequence. +found, using `is-eq` checks, in the provided sequence. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, for which the corresponding element types are, respectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. If the target item is not found in the sequence (or if an empty string or buffer is