From 6e281b7430596e11de028d3b62b4df328eab8a03 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 21 Jan 2021 15:26:00 -0600 Subject: [PATCH 01/42] Add `.clj-kondo/` to the gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f46b5a3..fe7582f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ pom.xml.asc *.iml .idea/ out -notes \ No newline at end of file +notes +.clj-kondo/ \ No newline at end of file From 7f5168769dbac43937209543c8e535773d8aa863 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 28 Jan 2021 16:25:00 -0600 Subject: [PATCH 02/42] Add methods to prevent numbers, vectors and strings from getting too big This should prevent numbers from getting too big or small, as well as prevent strings and vectors from getting too long. All the code was written by @mcgirjau and can be found here: https://github.com/mcgirjau/propeller/commit/b75e000a380b7d14b63bd05aa0ca76c388e73001 --- src/propeller/push/utils/globals.cljc | 21 +++++++++++- src/propeller/push/utils/helpers.cljc | 49 +++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/propeller/push/utils/globals.cljc b/src/propeller/push/utils/globals.cljc index 26c37f1..aad494b 100644 --- a/src/propeller/push/utils/globals.cljc +++ b/src/propeller/push/utils/globals.cljc @@ -8,4 +8,23 @@ ;; Limits the number of items that can be duplicated onto a stack at once. ;; We might want to extend this to limit all the different that things may be ;; placed on a stack. -(def max-stack-items 100) \ No newline at end of file +(def max-stack-items 100) + + + +;; ============================================================================= +;; Values used by the Push instructions to keep computed values within +;; reasonable size limits. +;; ============================================================================= + +;; Used by keep-number-reasonable as the maximum magnitude of any integer/float +(def max-number-magnitude 1.0E12) + +;; Used by keep-number-reasonable as the minimum magnitude of any float +(def min-number-magnitude 1.0E-10) + +;; Used by reasonable-string-length? to ensure that strings don't get too large +(def max-string-length 1000) + +;; Used by keep-vector-reasonable to ensure that vectors don't get too large +(def max-vector-length 1000) \ No newline at end of file diff --git a/src/propeller/push/utils/helpers.cljc b/src/propeller/push/utils/helpers.cljc index 4eccfb6..1596827 100755 --- a/src/propeller/push/utils/helpers.cljc +++ b/src/propeller/push/utils/helpers.cljc @@ -2,9 +2,40 @@ (:require [clojure.set] [propeller.push.core :as push] [propeller.push.state :as state] + [propeller.push.utils.globals :as globals] #?(:cljs [goog.string :as gstring]) #?(:cljs [goog.string.format]))) +;; Returns a version of the number n that is within reasonable size bounds +(defn keep-number-reasonable + [n] + (cond + (integer? n) + (cond + (> n globals/max-number-magnitude) (long globals/max-number-magnitude) + (< n (- globals/max-number-magnitude)) (long (- globals/max-number-magnitude)) + :else n) + :else + (cond + (Double/isNaN n) 0.0 + (or (= n Double/POSITIVE_INFINITY) + (> n globals/max-number-magnitude)) globals/max-number-magnitude + (or (= n Double/NEGATIVE_INFINITY) + (< n (- globals/max-number-magnitude))) (- globals/max-number-magnitude) + (< (- globals/min-number-magnitude) n globals/min-number-magnitude) 0.0 + :else n))) + +;; Returns true if the string is of a reasonable size +(defn reasonable-string-length? + [string] + (let [length (count string)] + (<= length globals/max-string-length))) + +;; Returns true if the vector is of a reasonable size +(defn reasonable-vector-length? + [vector] + (let [length (count vector)] + (<= length globals/max-vector-length))) ;; Takes a state and a collection of stacks to take args from. If there are ;; enough args on each of the desired stacks, returns a map with keys @@ -40,9 +71,23 @@ state (let [result (apply function (:args popped-args)) new-state (:state popped-args)] - (if (= result :ignore-instruction) + (cond + (number? result) + (state/push-to-stack new-state return-stack (keep-number-reasonable result)) + ;; + (and (string? result) + (not (reasonable-string-length? result))) state - (state/push-to-stack new-state return-stack result)))))) + ;; + (and (vector? result) + (not (reasonable-vector-length? result))) + state + ;; + (= result :ignore-instruction) + state + ;; + :else + (state/push-to-stack new-state return-stack result)))))) ;; Given a set of stacks, returns all instructions that operate on those stacks ;; only. Won't include random instructions unless :random is in the set as well From 156bbb91e39dfcbf06795e4e311ea214425cab2e Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 4 Mar 2021 16:02:31 -0600 Subject: [PATCH 03/42] Make limiting work in ClojureScript Change `keep-number-reasonable` so that it doesn't use Java's Double class when running in ClojureScript. I believe this should work, but I haven't tested it with ClojureScript yet. --- src/propeller/push/utils/helpers.cljc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/propeller/push/utils/helpers.cljc b/src/propeller/push/utils/helpers.cljc index 1596827..748f9eb 100755 --- a/src/propeller/push/utils/helpers.cljc +++ b/src/propeller/push/utils/helpers.cljc @@ -17,10 +17,13 @@ :else n) :else (cond - (Double/isNaN n) 0.0 - (or (= n Double/POSITIVE_INFINITY) + (#?(:clj Double/isNaN + :cljs js/isNaN) n) 0.0 + (or (= n #?(:clj Double/POSITIVE_INFINITY + :cljs js/Infinity)) (> n globals/max-number-magnitude)) globals/max-number-magnitude - (or (= n Double/NEGATIVE_INFINITY) + (or (= n #?(:clj Double/NEGATIVE_INFINITY + :cljs js/-Infinity)) (< n (- globals/max-number-magnitude))) (- globals/max-number-magnitude) (< (- globals/min-number-magnitude) n globals/min-number-magnitude) 0.0 :else n))) From e21ca24d9deda942d0f29e63c6cad0b5430722c4 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Tue, 16 Mar 2021 21:59:02 -0500 Subject: [PATCH 04/42] Add test for `string/butlast` This adds a test for the `string/butlast` instruction --- .../push/instructions/string_spec.clj | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 test/propeller/push/instructions/string_spec.clj diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj new file mode 100644 index 0000000..7f5b76d --- /dev/null +++ b/test/propeller/push/instructions/string_spec.clj @@ -0,0 +1,27 @@ +(ns propeller.push.instructions.string-spec + (:require + [clojure.test.check.generators :as gen] + [clojure.test.check.properties :as prop] + [clojure.test.check.clojure-test :as ct :refer [defspec]] + [propeller.push.state :as state] + [propeller.push.core :as core] + [propeller.push.instructions.string :as string])) + + +;; string/butlast + +(defn check-butlast + [value] + (let [start-state (state/push-to-stack state/empty-state + :string + value) + end-state ((:string_butlast @core/instruction-table) start-state) + expected-result (apply str (butlast value))] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec butlast-spec 100 + (prop/for-all [s gen/string] + (check-butlast s))) + + From d73b7c6117c3eabf4bdaf0dfcf3cab113f979943 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Tue, 16 Mar 2021 22:18:37 -0500 Subject: [PATCH 05/42] Add test for `string/concat` This adds a test for the `string/concat` instruction --- test/propeller/push/instructions/string_spec.clj | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 7f5b76d..dd4c803 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -25,3 +25,19 @@ (check-butlast s))) +;; string/concat + +(defn check-concat + [value1 value2] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value1) + (state/push-to-stack :string value2)) + end-state ((:string_concat @core/instruction-table) start-state) + expected-result (str value1 value2)] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec concat-spec 100 + (prop/for-all [s1 gen/string + s2 gen/string] + (check-concat s1 s2))) \ No newline at end of file From f028b7005d3b60cf7c2483142fb9890ec8efc8cf Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Tue, 16 Mar 2021 22:27:20 -0500 Subject: [PATCH 06/42] Add test for `string/conj-char` This adds a test for the `string/conj-char` instruction --- .../push/instructions/string_spec.clj | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index dd4c803..8b4dc61 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -40,4 +40,22 @@ (defspec concat-spec 100 (prop/for-all [s1 gen/string s2 gen/string] - (check-concat s1 s2))) \ No newline at end of file + (check-concat s1 s2))) + + +;; string/conj-char + +(defn check-conj-char + [value char] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value) + (state/push-to-stack :char char)) + end-state ((:string_concat @core/instruction-table) start-state) + expected-result (str value char)] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec conj-char-spec 100 + (prop/for-all [str gen/string + char gen/char] + (check-concat str char))) \ No newline at end of file From 227a41b3762703c1fb5110bda9a1113c69f164c8 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Tue, 16 Mar 2021 22:43:59 -0500 Subject: [PATCH 07/42] Add test for `string/contains` This adds a test for the `string/contains` instruction --- .../push/instructions/string_spec.clj | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 8b4dc61..10700a4 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -58,4 +58,22 @@ (defspec conj-char-spec 100 (prop/for-all [str gen/string char gen/char] - (check-concat str char))) \ No newline at end of file + (check-concat str char))) + + +;; string/contains + +(defn check-contains + [value1 value2] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value1) + (state/push-to-stack :string value2)) + end-state ((:string_contains @core/instruction-table) start-state) + expected-result (string/includes? value2 value1)] + (= expected-result + (state/peek-stack end-state :boolean)))) + +(defspec contains-spec 100 + (prop/for-all [str1 gen/string + str2 gen/string] + (check-contains str1 str2))) \ No newline at end of file From ca311c9f36499c811964be6d6fb0be749c1013a0 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Tue, 16 Mar 2021 22:47:41 -0500 Subject: [PATCH 08/42] Fix `string/contains` This fixes `string/contains` to be consistent with it's descriptions, as well as it's implementation in Clojush --- src/propeller/push/instructions/string.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/propeller/push/instructions/string.cljc b/src/propeller/push/instructions/string.cljc index 7d88e03..445373b 100755 --- a/src/propeller/push/instructions/string.cljc +++ b/src/propeller/push/instructions/string.cljc @@ -38,7 +38,7 @@ :string_contains ^{:stacks #{:boolean :string}} (fn [state] - (make-instruction state string/includes? [:string :string] :boolean))) + (make-instruction state #(string/includes? %2 %1) [:string :string] :boolean))) ;; Pushes TRUE if the top CHAR is contained in the top STRING, and FALSE ;; otherwise From 54e09010405cece33864c8225f0b675c012d0408 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Tue, 16 Mar 2021 22:49:49 -0500 Subject: [PATCH 09/42] Change naming of some variables and fix `string/conj-char` This fixes `string/conj-char` to actually run the correct instruction instead of `string/concat`. Additionally, changes the name of some variables. --- test/propeller/push/instructions/string_spec.clj | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 10700a4..a10936c 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -1,11 +1,12 @@ (ns propeller.push.instructions.string-spec (:require + [clojure.string :as string] [clojure.test.check.generators :as gen] [clojure.test.check.properties :as prop] [clojure.test.check.clojure-test :as ct :refer [defspec]] [propeller.push.state :as state] [propeller.push.core :as core] - [propeller.push.instructions.string :as string])) + [propeller.push.instructions.string :as string-instructions])) ;; string/butlast @@ -21,8 +22,8 @@ (state/peek-stack end-state :string)))) (defspec butlast-spec 100 - (prop/for-all [s gen/string] - (check-butlast s))) + (prop/for-all [str gen/string] + (check-butlast str))) ;; string/concat @@ -38,9 +39,9 @@ (state/peek-stack end-state :string)))) (defspec concat-spec 100 - (prop/for-all [s1 gen/string - s2 gen/string] - (check-concat s1 s2))) + (prop/for-all [str1 gen/string + str2 gen/string] + (check-concat str1 str2))) ;; string/conj-char @@ -50,7 +51,7 @@ (let [start-state (-> state/empty-state (state/push-to-stack :string value) (state/push-to-stack :char char)) - end-state ((:string_concat @core/instruction-table) start-state) + end-state ((:string_conj-char @core/instruction-table) start-state) expected-result (str value char)] (= expected-result (state/peek-stack end-state :string)))) From 54c44a49b642aed16e739c1c5f4207a69890c5ec Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Tue, 16 Mar 2021 22:54:07 -0500 Subject: [PATCH 10/42] Add test for `string/contains-char` This adds a test for the `string/contains-char` instruction --- .../push/instructions/string_spec.clj | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index a10936c..c4a9486 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -77,4 +77,22 @@ (defspec contains-spec 100 (prop/for-all [str1 gen/string str2 gen/string] - (check-contains str1 str2))) \ No newline at end of file + (check-contains str1 str2))) + + +;; string/contains-char + +(defn check-contains-char + [value char] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value) + (state/push-to-stack :char char)) + end-state ((:string_contains-char @core/instruction-table) start-state) + expected-result (string/includes? value char)] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec contains-char-spec 100 + (prop/for-all [str gen/string + char gen/string] + (check-concat str char))) \ No newline at end of file From f14d738669809d6811d3e873fe1cef398d7ea44c Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Tue, 16 Mar 2021 23:22:04 -0500 Subject: [PATCH 11/42] Fix some issues with the `string/contains-char` test Fixes the test to actually run `check-contains-char` as well as some issues with that function. Also fixes similar problems with the `string/conj-char` test. --- test/propeller/push/instructions/string_spec.clj | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index c4a9486..493f087 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -51,7 +51,7 @@ (let [start-state (-> state/empty-state (state/push-to-stack :string value) (state/push-to-stack :char char)) - end-state ((:string_conj-char @core/instruction-table) start-state) + end-state ((:string_conj_char @core/instruction-table) start-state) expected-result (str value char)] (= expected-result (state/peek-stack end-state :string)))) @@ -59,7 +59,7 @@ (defspec conj-char-spec 100 (prop/for-all [str gen/string char gen/char] - (check-concat str char))) + (check-conj-char str char))) ;; string/contains @@ -87,9 +87,15 @@ (let [start-state (-> state/empty-state (state/push-to-stack :string value) (state/push-to-stack :char char)) - end-state ((:string_contains-char @core/instruction-table) start-state) - expected-result (string/includes? value char)] + end-state ((:string_contains_char @core/instruction-table) start-state) + expected-result (string/includes? value (str char))] (= expected-result + (state/peek-stack end-state :boolean)))) + +(defspec contains-char-spec 100 + (prop/for-all [str gen/string + char gen/char] + (check-contains-char str char))) (state/peek-stack end-state :string)))) (defspec contains-char-spec 100 From 19b67cdfecbf85ea040f5c2de5bd5a666c2b389e Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Tue, 16 Mar 2021 23:22:40 -0500 Subject: [PATCH 12/42] Add test for `string/drop` This adds a test for the `string/drop` instruction --- .../push/instructions/string_spec.clj | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 493f087..50ea2de 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -96,9 +96,21 @@ (prop/for-all [str gen/string char gen/char] (check-contains-char str char))) + + +;; string/drop + +(defn check-drop + [value n] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value) + (state/push-to-stack :integer n)) + end-state ((:string_drop @core/instruction-table) start-state) + expected-result (apply str (drop n value))] + (= expected-result (state/peek-stack end-state :string)))) -(defspec contains-char-spec 100 +(defspec drop-spec 100 (prop/for-all [str gen/string - char gen/string] - (check-concat str char))) \ No newline at end of file + int gen/small-integer] + (check-drop str int))) \ No newline at end of file From 50804b7cb879aee2b513f0d613908e361da3609f Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 18 Mar 2021 22:32:11 -0500 Subject: [PATCH 13/42] Add test for `string/empty-string` This adds a test for the `string/empty-string` instruction --- .../propeller/push/instructions/string_spec.clj | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 50ea2de..18af4b2 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -113,4 +113,19 @@ (defspec drop-spec 100 (prop/for-all [str gen/string int gen/small-integer] - (check-drop str int))) \ No newline at end of file + (check-drop str int))) + + +;; string/empty-string + +(defn check-empty-string + [value] + (let [start-state (state/push-to-stack state/empty-state :string value) + end-state ((:string_empty_string @core/instruction-table) start-state) + expected-result (empty? value)] + (= expected-result + (state/peek-stack end-state :boolean)))) + +(defspec empty-string-spec 100 + (prop/for-all [str gen/string] + (check-empty-string str))) \ No newline at end of file From 2008712e0afc857cdfccb4f32da74f022bc9dc56 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 18 Mar 2021 22:48:15 -0500 Subject: [PATCH 14/42] Add test for `string/first` This adds a test for the `string/first` instruction --- .../push/instructions/string_spec.clj | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 18af4b2..e9e0f6a 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -128,4 +128,24 @@ (defspec empty-string-spec 100 (prop/for-all [str gen/string] - (check-empty-string str))) \ No newline at end of file + (check-empty-string str))) + + +;; string/first + +(defn check-first + [value] + (let [start-state (state/push-to-stack state/empty-state :string value) + end-state ((:string_first @core/instruction-table) start-state) + expected-result (first value)] + (or + (and (empty? value) + (= (state/peek-stack end-state :string) value) + (state/empty-stack? end-state :char)) + (and (= expected-result + (state/peek-stack end-state :char)) + (state/empty-stack? end-state :string))))) + +(defspec first-spec 100 + (prop/for-all [str gen/string] + (check-first str))) \ No newline at end of file From 39a25fa7e30183af160e1ecb4b71cb096640c06d Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 18 Mar 2021 22:49:44 -0500 Subject: [PATCH 15/42] Change the `string/first` instruction This changes the `string/first` instruction to return :ignore-instruction if the string is empty. --- src/propeller/push/instructions/string.cljc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/propeller/push/instructions/string.cljc b/src/propeller/push/instructions/string.cljc index 445373b..f8ddeae 100755 --- a/src/propeller/push/instructions/string.cljc +++ b/src/propeller/push/instructions/string.cljc @@ -68,7 +68,9 @@ :string_first ^{:stacks #{:char :string}} (fn [state] - (make-instruction state first [:string] :char))) + (make-instruction state + #(if (empty? %) :ignore-instruction (first %)) + [:string] :char))) ;; Pushes the STRING version of the top BOOLEAN, e.g. "true" (def-instruction From 196f39ccb6f86870bff7cf2ee48c174cd7fe531a Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 18 Mar 2021 23:01:09 -0500 Subject: [PATCH 16/42] Add a test for `string/from-boolean` This adds a test for the `string/from-boolean` instruction --- .../propeller/push/instructions/string_spec.clj | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index e9e0f6a..1f29a4f 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -148,4 +148,19 @@ (defspec first-spec 100 (prop/for-all [str gen/string] - (check-first str))) \ No newline at end of file + (check-first str))) + + +;; string/from-boolean + +(defn check-from-boolean + [value] + (let [start-state (state/push-to-stack state/empty-state :boolean value) + end-state ((:string_from_boolean @core/instruction-table) start-state) + expected-result (str value)] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec from-boolean-spec 10 + (prop/for-all [bool gen/boolean] + (check-from-boolean bool))) \ No newline at end of file From d3b876492c2d640c8322d7336beefdc6eca74082 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 18 Mar 2021 23:05:13 -0500 Subject: [PATCH 17/42] Add a test for `string/from-char` This adds a test for the `string/from-char` instruction --- .../propeller/push/instructions/string_spec.clj | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 1f29a4f..6c2b571 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -163,4 +163,19 @@ (defspec from-boolean-spec 10 (prop/for-all [bool gen/boolean] - (check-from-boolean bool))) \ No newline at end of file + (check-from-boolean bool))) + + +;; string/from-char + +(defn check-from-char + [value] + (let [start-state (state/push-to-stack state/empty-state :char value) + end-state ((:string_from_char @core/instruction-table) start-state) + expected-result (str value)] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec from-char-spec 10 + (prop/for-all [char gen/char] + (check-from-char char))) \ No newline at end of file From 2da8ef6de52c3a2b76f898bda0b82b40649f6c79 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 18 Mar 2021 23:11:31 -0500 Subject: [PATCH 18/42] Add a test for `string/from-char` This adds a test for the `string/from-char` instruction --- .../push/instructions/string_spec.clj | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 6c2b571..4349e8f 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -176,6 +176,21 @@ (= expected-result (state/peek-stack end-state :string)))) -(defspec from-char-spec 10 +(defspec from-char-spec 100 (prop/for-all [char gen/char] - (check-from-char char))) \ No newline at end of file + (check-from-char char))) + + +;; string/from-float + +(defn check-from-float + [value] + (let [start-state (state/push-to-stack state/empty-state :float value) + end-state ((:string_from_float @core/instruction-table) start-state) + expected-result (str value)] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec from-float-spec 100 + (prop/for-all [float gen/double] + (check-from-boolean float))) \ No newline at end of file From b8a011c385ebabb1afe677a422fea426005395e6 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 18 Mar 2021 23:16:41 -0500 Subject: [PATCH 19/42] Add a test for `string/from-float` This adds a test for the `string/from-float` instruction --- .../propeller/push/instructions/string_spec.clj | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 4349e8f..c32ae2c 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -193,4 +193,19 @@ (defspec from-float-spec 100 (prop/for-all [float gen/double] - (check-from-boolean float))) \ No newline at end of file + (check-from-float float))) + + +;; string/from-integer + +(defn check-from-integer + [value] + (let [start-state (state/push-to-stack state/empty-state :integer value) + end-state ((:string_from_integer @core/instruction-table) start-state) + expected-result (str value)] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec from-integer-spec 100 + (prop/for-all [int gen/small-integer] + (check-from-integer int))) \ No newline at end of file From 92cdfe9e590db8ae78eef809a3c5fe685a13d68a Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Sun, 21 Mar 2021 00:39:25 -0500 Subject: [PATCH 20/42] Add a test for `string/indexof-char` This adds a test for the `string/indexof-char` instruction --- .../push/instructions/string_spec.clj | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index c32ae2c..363f4fa 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -208,4 +208,27 @@ (defspec from-integer-spec 100 (prop/for-all [int gen/small-integer] - (check-from-integer int))) \ No newline at end of file + (check-from-integer int))) + + +;; string/indexof-char + +(defn check-indexof-char + [value char] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value) + (state/push-to-stack :char char)) + end-state ((:string_indexof_char @core/instruction-table) start-state) + expected-result (string/index-of value char)] + (or + (and (not expected-result) + (= (state/peek-stack end-state :string) value) + (= (state/peek-stack end-state :char) char) + (state/empty-stack? end-state :integer)) + (= expected-result + (state/peek-stack end-state :integer))))) + +(defspec indexof-char-spec 100 + (prop/for-all [str gen/string + char gen/char] + (check-indexof-char str char))) \ No newline at end of file From 9cd4f875c2dbe91c216285b088cb11dfa331bf70 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Sun, 21 Mar 2021 01:02:11 -0500 Subject: [PATCH 21/42] Fix `string/indexof-char` Changes `string/indexof-char` to return :ignore-instruction if the character is not present in the string. This makes it consistent with it's description --- src/propeller/push/instructions/string.cljc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/propeller/push/instructions/string.cljc b/src/propeller/push/instructions/string.cljc index f8ddeae..46d9811 100755 --- a/src/propeller/push/instructions/string.cljc +++ b/src/propeller/push/instructions/string.cljc @@ -106,7 +106,11 @@ :string_indexof_char ^{:stacks #{:char :integer :string}} (fn [state] - (make-instruction state string/index-of [:string :char] :integer))) + (make-instruction state + #(let [index (string/index-of %1 %2)] + (if index index :ignore-instruction)) + [:string :char] + :integer))) ;; Iterates over the top STRING using code on the EXEC stack (def-instruction From c270ce6c1a6ac5676a03c9c08a9725cb0359ef36 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Tue, 23 Mar 2021 22:18:28 -0500 Subject: [PATCH 22/42] Add test for `string/last` This adds a test for the `string/last` instruction --- .../propeller/push/instructions/string_spec.clj | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 363f4fa..07c9f48 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -231,4 +231,19 @@ (defspec indexof-char-spec 100 (prop/for-all [str gen/string char gen/char] - (check-indexof-char str char))) \ No newline at end of file + (check-indexof-char str char))) + + +;; string/last + +(defn check-last + [value] + (let [start-state (state/push-to-stack state/empty-state :string value) + end-state ((:string_last @core/instruction-table) start-state) + expected-result (last value)] + (= expected-result + (state/peek-stack end-state :char)))) + +(defspec last-spec 100 + (prop/for-all [str gen/string] + (check-last str))) \ No newline at end of file From acdc225c71ebd35c950beb56af2f8bfc379cab62 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Tue, 23 Mar 2021 22:30:18 -0500 Subject: [PATCH 23/42] Add a test for `string/length` This adds a test for the `string/length` instruction --- .../propeller/push/instructions/string_spec.clj | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 07c9f48..0a52ecf 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -246,4 +246,19 @@ (defspec last-spec 100 (prop/for-all [str gen/string] - (check-last str))) \ No newline at end of file + (check-last str))) + + +;; string/length + +(defn check-length + [value] + (let [start-state (state/push-to-stack state/empty-state :string value) + end-state ((:string_length @core/instruction-table) start-state) + expected-result (count value)] + (= expected-result + (state/peek-stack end-state :integer)))) + +(defspec length-spec 100 + (prop/for-all [str gen/string] + (check-length str))) \ No newline at end of file From 7aad934b9676694d08cec266e6d44c2f7d453ea2 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Tue, 23 Mar 2021 22:42:49 -0500 Subject: [PATCH 24/42] Change `string/last` Changes `string/last` to return `:ignore-instruction` if the given string is empty --- src/propeller/push/instructions/string.cljc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/propeller/push/instructions/string.cljc b/src/propeller/push/instructions/string.cljc index 46d9811..5218ff5 100755 --- a/src/propeller/push/instructions/string.cljc +++ b/src/propeller/push/instructions/string.cljc @@ -136,12 +136,13 @@ (state/push-to-stack :exec (state/peek-stack state :exec)) (state/push-to-stack :char (first top-item)))))))) -;; Pushes the last CHAR of the top STRING +;; Pushes the last CHAR of the top STRING. +;; If the string is empty, do nothing (def-instruction :string_last ^{:stacks #{:char :string}} (fn [state] - (make-instruction state last [:string] :char))) + (make-instruction state #(if (empty? %) :ignore-instruction (last %)) [:string] :char))) ;; Pushes the length of the top STRING onto the INTEGER stack (def-instruction From da117c2f08b51925df5431a5867318862861bee0 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Tue, 23 Mar 2021 22:47:46 -0500 Subject: [PATCH 25/42] Update the test for `string/length` Changes the test for the `string/length` instruction to check for an `:ignore-instruction` when the string is empty. --- test/propeller/push/instructions/string_spec.clj | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 0a52ecf..622f530 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -241,8 +241,13 @@ (let [start-state (state/push-to-stack state/empty-state :string value) end-state ((:string_last @core/instruction-table) start-state) expected-result (last value)] - (= expected-result - (state/peek-stack end-state :char)))) + (or + (and (empty? value) + (state/empty-stack? end-state :char) + (= value (state/peek-stack end-state :string))) + (and (state/empty-stack? end-state :string) + (= expected-result + (state/peek-stack end-state :char)))))) (defspec last-spec 100 (prop/for-all [str gen/string] From 8e361225aeb93d771d79eb0933ba87b58f518bbc Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Mon, 29 Mar 2021 21:01:27 -0500 Subject: [PATCH 26/42] Add test for `string/nth` This adds a test for the `string/nth` instruction --- .../push/instructions/string_spec.clj | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 622f530..4f32de4 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -266,4 +266,26 @@ (defspec length-spec 100 (prop/for-all [str gen/string] - (check-length str))) \ No newline at end of file + (check-length str))) + + +;; string/nth + +(defn check-nth + [value n] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value) + (state/push-to-stack :integer n)) + end-state ((:string_nth @core/instruction-table) start-state)] + (or + (and (empty? value) + (state/empty-stack? end-state :char) + (= value (state/peek-stack end-state :string)) + (= n (state/peek-stack end-state :integer))) + (= (nth value (mod n (count value))) + (state/peek-stack end-state :char))))) + +(defspec nth-spec 100 + (prop/for-all [str gen/string + int gen/small-integer] + (check-nth str int))) \ No newline at end of file From 695e08506ed7c864b628a0df5bba393f1264bd61 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Mon, 29 Mar 2021 21:05:35 -0500 Subject: [PATCH 27/42] Fix `string/nth` instruction This changes `string/nth` to return `:ignore-instruction` when the string is empty. Otherwise it would throw an error due to dividing by zero. --- src/propeller/push/instructions/string.cljc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/propeller/push/instructions/string.cljc b/src/propeller/push/instructions/string.cljc index 5218ff5..26398a7 100755 --- a/src/propeller/push/instructions/string.cljc +++ b/src/propeller/push/instructions/string.cljc @@ -158,7 +158,13 @@ :string_nth ^{:stacks #{:char :integer :string}} (fn [state] - (make-instruction state #(nth %2 (mod %1 (count %2))) [:integer :string] :char))) + (make-instruction state + #(let [str-length (count %2)] + (if (= 0 str-length) + :ignore-instruction + (nth %2 (mod %1 str-length)))) + [:integer :string] + :char))) ;; Pushes the number of times the top CHAR occurs in the top STRING onto the ;; INTEGER stack From 0655a5748fa26368ccdbcc0a03aef1239b245273 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 1 Apr 2021 10:49:57 -0500 Subject: [PATCH 28/42] Add a test for `string/occurencesof_char` This adds a test for the `string/occurencesof_char` instruction. --- .../push/instructions/string_spec.clj | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 4f32de4..10e6343 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -234,6 +234,9 @@ (check-indexof-char str char))) +;; string/iterate + + ;; string/last (defn check-last @@ -288,4 +291,22 @@ (defspec nth-spec 100 (prop/for-all [str gen/string int gen/small-integer] - (check-nth str int))) \ No newline at end of file + (check-nth str int))) + + +;; string/occurencesof_char + +(defn check-occurencesof-char + [value char] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value) + (state/push-to-stack :char char)) + end-state ((:string_occurencesof_char @core/instruction-table) start-state) + expected-result (count (filter #(= char %) value))] + (= expected-result + (state/peek-stack end-state :integer)))) + +(defspec occurencesof-char-spec 100 + (prop/for-all [str gen/string + char gen/char] + (check-occurencesof-char str char))) \ No newline at end of file From 95cab605763c4a6f69d6e750c2f74e8a46641e5f Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 1 Apr 2021 10:55:21 -0500 Subject: [PATCH 29/42] Add a test for `string/parse-to-chars` This adds a test for the `string/parse-to-chars` instruction. --- .../push/instructions/string_spec.clj | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 10e6343..0c92c0e 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -309,4 +309,25 @@ (defspec occurencesof-char-spec 100 (prop/for-all [str gen/string char gen/char] - (check-occurencesof-char str char))) \ No newline at end of file + (check-occurencesof-char str char))) + + +;; string/parse-to-chars + +(defn check-parse-to-chars + [value] + (let [start-state (state/push-to-stack state/empty-state :string value) + end-state ((:string_parse_to_chars @core/instruction-table) start-state) + ;; Since split will return the empty string when given the empty string + string-length (if (= 0 (count value)) 1 (count value)) + expected-result (string/split value #"")] + (and + (= expected-result + (state/peek-stack-many end-state :string string-length)) + (-> end-state + (state/pop-stack-many :string string-length) + (state/empty-stack? :string))))) + +(defspec parse-to-chars-spec 100 + (prop/for-all [str gen/string] + (check-parse-to-chars str))) From da22da5014138840e33693e6134f379261593603 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 1 Apr 2021 10:59:38 -0500 Subject: [PATCH 30/42] Add a test for `string/remove-char` This adds a test for the `string/remove-char` instruction --- .../push/instructions/string_spec.clj | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 0c92c0e..f29109a 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -331,3 +331,21 @@ (defspec parse-to-chars-spec 100 (prop/for-all [str gen/string] (check-parse-to-chars str))) + + +;; string/remove-char + +(defn check-remove-char + [value char] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value) + (state/push-to-stack :char char)) + end-state ((:string_remove_char @core/instruction-table) start-state) + expected-result (apply str (filter #(not= char %) value))] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec remove-char-spec 100 + (prop/for-all [str gen/string + char gen/char] + (check-remove-char str char))) From 95cad31e7b57311d5acf16f6dff45d409b7e473c Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 1 Apr 2021 11:07:01 -0500 Subject: [PATCH 31/42] Add a test for `string/replace` This adds a test for the `string/replace` instruction --- .../push/instructions/string_spec.clj | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index f29109a..8f96c70 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -349,3 +349,23 @@ (prop/for-all [str gen/string char gen/char] (check-remove-char str char))) + + +;; string/replace + +(defn check-replace + [value1 value2 value3] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value1) + (state/push-to-stack :string value2) + (state/push-to-stack :string value3)) + end-state ((:string_replace @core/instruction-table) start-state) + expected-result (string/replace value1 value2 value3)] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec replace-spec 100 + (prop/for-all [str1 gen/string + str2 gen/string + str3 gen/string] + (check-replace str1 str2 str3))) From 3fb35d7d472ce4bdae9e462f386504455427a7d8 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 1 Apr 2021 11:12:26 -0500 Subject: [PATCH 32/42] Add a test for `string/replace-char` This adds a test for the `string/replace-char` instruction --- .../push/instructions/string_spec.clj | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 8f96c70..f95714f 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -369,3 +369,23 @@ str2 gen/string str3 gen/string] (check-replace str1 str2 str3))) + + +;; string/replace-char + +(defn check-replace-char + [value char1 char2] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value) + (state/push-to-stack :char char1) + (state/push-to-stack :char char2)) + end-state ((:string_replace_char @core/instruction-table) start-state) + expected-result (string/replace value char1 char2)] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec replace-char-spec 100 + (prop/for-all [str gen/string + char1 gen/char + char2 gen/char] + (check-replace str char1 char2))) \ No newline at end of file From 110679d8f725bd7f5ec25da171ee52c21c144e86 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 1 Apr 2021 11:15:40 -0500 Subject: [PATCH 33/42] Add a test for `string/replace-first` This adds a test for the `string/replace-first` instruction --- .../push/instructions/string_spec.clj | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index f95714f..0218ac0 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -388,4 +388,24 @@ (prop/for-all [str gen/string char1 gen/char char2 gen/char] - (check-replace str char1 char2))) \ No newline at end of file + (check-replace str char1 char2))) + + +;; string/replace-first + +(defn check-replace-first + [value1 value2 value3] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value1) + (state/push-to-stack :string value2) + (state/push-to-stack :string value3)) + end-state ((:string_replace_first @core/instruction-table) start-state) + expected-result (string/replace-first value1 value2 value3)] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec replace-first-spec 100 + (prop/for-all [str1 gen/string + str2 gen/string + str3 gen/string] + (check-replace-first str1 str2 str3))) \ No newline at end of file From 3cdc2f7fe5887c4e82249acc8d7059eb7ff06afc Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 1 Apr 2021 11:19:09 -0500 Subject: [PATCH 34/42] Add a test for `string/replace-first-char` This adds a test for the `string/replace-first-char` instruction --- .../push/instructions/string_spec.clj | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 0218ac0..a690001 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -408,4 +408,24 @@ (prop/for-all [str1 gen/string str2 gen/string str3 gen/string] - (check-replace-first str1 str2 str3))) \ No newline at end of file + (check-replace-first str1 str2 str3))) + + +;; string/replace-first-char + +(defn check-replace-first-char + [value char1 char2] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value) + (state/push-to-stack :char char1) + (state/push-to-stack :char char2)) + end-state ((:string_replace_first_char @core/instruction-table) start-state) + expected-result (string/replace-first value char1 char2)] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec replace-first-char-spec 100 + (prop/for-all [str gen/string + char1 gen/char + char2 gen/char] + (check-replace-first-char str char1 char2))) \ No newline at end of file From 5cc3a02002f86a67c2dde2169fed9e24ba71313f Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 1 Apr 2021 11:20:40 -0500 Subject: [PATCH 35/42] Fix `replace-char-spec` to use the correct function This changes `replace-char-spec` to actually use `check-replace-char` instead of `check-replace` --- test/propeller/push/instructions/string_spec.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index a690001..21d3281 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -388,7 +388,7 @@ (prop/for-all [str gen/string char1 gen/char char2 gen/char] - (check-replace str char1 char2))) + (check-replace-char str char1 char2))) ;; string/replace-first From 7e780567ec7c1060e33af92b2015b23fd1c9e672 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 1 Apr 2021 11:26:55 -0500 Subject: [PATCH 36/42] Add test for `string/rest` This adds a test for the `string/rest` instruction --- .../propeller/push/instructions/string_spec.clj | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 21d3281..64707c6 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -428,4 +428,19 @@ (prop/for-all [str gen/string char1 gen/char char2 gen/char] - (check-replace-first-char str char1 char2))) \ No newline at end of file + (check-replace-first-char str char1 char2))) + + +;; string/rest + +(defn check-rest + [value] + (let [start-state (state/push-to-stack state/empty-state :string value) + end-state ((:string_rest @core/instruction-table) start-state) + expected-result (apply str (rest value))] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec rest-spec 100 + (prop/for-all [str gen/string] + (check-rest str))) From 711fc20f017f9f05da7912ddb4eb2d119fc415e0 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 1 Apr 2021 11:30:02 -0500 Subject: [PATCH 37/42] Add a test for `string/reverse` This adds a test for the `string/reverse` instruction --- test/propeller/push/instructions/string_spec.clj | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 64707c6..30c738c 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -444,3 +444,18 @@ (defspec rest-spec 100 (prop/for-all [str gen/string] (check-rest str))) + + +;; string/reverse + +(defn check-reverse + [value] + (let [start-state (state/push-to-stack state/empty-state :string value) + end-state ((:string_reverse @core/instruction-table) start-state) + expected-result (apply str (reverse value))] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec reverse-spec 100 + (prop/for-all [str gen/string] + (check-reverse str))) \ No newline at end of file From 27bd7b6eb6018190bd727ecb64d6a0568f4e2c8f Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 1 Apr 2021 12:03:34 -0500 Subject: [PATCH 38/42] Add a test for `string/set-char` This adds a test for the `string/set-char` instruction --- .../push/instructions/string_spec.clj | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 30c738c..f38aad8 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -458,4 +458,33 @@ (defspec reverse-spec 100 (prop/for-all [str gen/string] - (check-reverse str))) \ No newline at end of file + (check-reverse str))) + + +;; string/set-char + +(defn check-set-char + [value char n] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value) + (state/push-to-stack :char char) + (state/push-to-stack :integer n)) + end-state ((:string_set_char @core/instruction-table) start-state)] + (or + (and + (empty? value) + (= (state/peek-stack end-state :string) value) + (= (state/peek-stack end-state :char) char) + (= (state/peek-stack end-state :integer) n)) + (= + (let [index (mod n (count value)) + start (subs value 0 index) + end (subs value (+ index 1))] + (str start char end)) + (state/peek-stack end-state :string))))) + +(defspec set-char-spec 100 + (prop/for-all [str gen/string + char gen/char + int gen/small-integer] + (check-set-char str char int))) From e3d7963355786048b5be162c6b217c67d9c73c8f Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 1 Apr 2021 12:04:02 -0500 Subject: [PATCH 39/42] Fix `string/set-char` This changes `string/set-char` to throw `:ignore-instruction` when the string is empty. Otherwise the instruction throws a Divide by Zero error. --- src/propeller/push/instructions/string.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/propeller/push/instructions/string.cljc b/src/propeller/push/instructions/string.cljc index 26398a7..c03ca2e 100755 --- a/src/propeller/push/instructions/string.cljc +++ b/src/propeller/push/instructions/string.cljc @@ -268,10 +268,12 @@ ^{:stacks #{:char :integer :string}} (fn [state] (make-instruction state - #(let [index (mod %2 (count %3)) + #(if (empty? %3) + :ignore-instruction + (let [index (mod %2 (count %3)) beginning (take index %3) end (drop (inc index) %3)] - (apply str (concat beginning (list %1) end))) + (apply str (concat beginning (list %1) end)))) [:char :integer :string] :string))) From 1628e669b6f9ef57e7af58285fe020b3abad6670 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 15 Apr 2021 15:12:42 -0500 Subject: [PATCH 40/42] Add a test for `string/split` This adds a test for the `string/split` instruction --- .../push/instructions/string_spec.clj | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index f38aad8..d5e6e99 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -488,3 +488,23 @@ char gen/char int gen/small-integer] (check-set-char str char int))) + + +;; string/split + +(defn check-split + [value] + (let [start-state (state/push-to-stack state/empty-state :string value) + end-state ((:string_split @core/instruction-table) start-state) + our-split (string/split (string/trim value) #"\s+") + num-items (count our-split)] + (and + (= (state/stack-size end-state :string) num-items) + (every? identity + (map = + our-split + (state/peek-stack-many end-state :string num-items)))))) + +(defspec split-spec 100 + (prop/for-all [str gen/string] + (check-split str))) \ No newline at end of file From 07ef1d3f9046ba44656e652797e52ac71d4067c0 Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 15 Apr 2021 16:03:57 -0500 Subject: [PATCH 41/42] Add tests for `string/substr` and `str/take` This adds tests for the `string/substr` and `str/take` instructions. --- .../push/instructions/string_spec.clj | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index d5e6e99..11d0b78 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -507,4 +507,36 @@ (defspec split-spec 100 (prop/for-all [str gen/string] - (check-split str))) \ No newline at end of file + (check-split str))) + + +;; string/substr + +(defn check-substr + [instruction value start end] + (let [start-state (-> state/empty-state + (state/push-to-stack :string value) + (state/push-to-stack :integer start) + (state/push-to-stack :integer end)) + end-state ((instruction @core/instruction-table) start-state) + str-len (count value) + small (min str-len (max 0 start)) + big (min str-len (max 0 small end)) + expected-result (subs value small big)] + (= expected-result + (state/peek-stack end-state :string)))) + +(defspec substr-spec 100 + (prop/for-all + [tuple (gen/let [str gen/string + int1 (gen/large-integer* {:min -5 :max (+ 5 (count str))}) + int2 (gen/large-integer* {:min -5 :max (+ 5 (count str))})] + [:string_substr, str, int1, int2])] + (apply check-substr tuple))) + +(defspec take-spec 100 + (prop/for-all + [tuple (gen/let [str gen/string + int (gen/large-integer* {:min -5 :max (+ 5 (count str))})] + [:string_take, str, 0, int])] + (apply check-substr tuple))) \ No newline at end of file From 24630072ed64509be81f816012cc310eb7978c9d Mon Sep 17 00:00:00 2001 From: Erik Rauer Date: Thu, 15 Apr 2021 16:24:06 -0500 Subject: [PATCH 42/42] Add tests for `string/iterate` This adds a test for the `string/iterate` instruction --- .../push/instructions/string_spec.clj | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test/propeller/push/instructions/string_spec.clj b/test/propeller/push/instructions/string_spec.clj index 11d0b78..d0b00fb 100644 --- a/test/propeller/push/instructions/string_spec.clj +++ b/test/propeller/push/instructions/string_spec.clj @@ -6,7 +6,8 @@ [clojure.test.check.clojure-test :as ct :refer [defspec]] [propeller.push.state :as state] [propeller.push.core :as core] - [propeller.push.instructions.string :as string-instructions])) + [propeller.push.instructions.string :as string-instructions] + [propeller.push.interpreter :as interpreter])) ;; string/butlast @@ -236,6 +237,23 @@ ;; string/iterate +(defn check-iterate + [value] + (let [print-instr (keyword "char_print") + iter-instr (keyword "string_iterate") + program [iter-instr print-instr] + start-state (-> state/empty-state + (state/push-to-stack :string value) + (state/push-to-stack :output "")) + ; 4 times the string length should be enough for this iteration, perhaps even + ; more than we strictly need. + end-state (interpreter/interpret-program program start-state (* 4 (count value)))] + (= value + (state/peek-stack end-state :output)))) + +(defspec iterate-spec 100 + (prop/for-all [value gen/string] + (check-iterate value))) ;; string/last