From 89f0f3568ab960d48ec15ba230ddf0be242b6d96 Mon Sep 17 00:00:00 2001 From: Nic McPhee Date: Tue, 10 Nov 2020 16:14:23 -0600 Subject: [PATCH 1/3] Add `stack-size` Adds a function that returns the size of the specified stack in a given state. --- src/propeller/push/state.cljc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/propeller/push/state.cljc b/src/propeller/push/state.cljc index 5026dec..f6c0490 100755 --- a/src/propeller/push/state.cljc +++ b/src/propeller/push/state.cljc @@ -35,6 +35,11 @@ [state stack] (empty? (get state stack))) +;; Returns the stack size +(defn stack-size + [state stack] + (count (get state stack))) + ;; Returns the top item on the stack (defn peek-stack [state stack] From 1a882836dea29c123640ddfcd4f92092cedcfdb5 Mon Sep 17 00:00:00 2001 From: Nic McPhee Date: Tue, 10 Nov 2020 16:17:50 -0600 Subject: [PATCH 2/3] Limit number of duplications onto a stack Both `_dup_times` and `_dup_items` are risky and can place very large numbers of items on a stack if the value on the `:integer` is large. This limits both `_dup_times` and `_dup_items` so they never make a stack have more than `max-stack-items` entries. This change was motivated by `OutOfMemory` errors we were receiving when doing runs. This is somewhat modeled after similar limits in Clojush, but we chose to not introduce an `atom` and to instead just define a constant in this namespace. That may not be as flexible as people would like, and we can move to the `atom` solution if necessary. --- src/propeller/push/instructions/polymorphic.cljc | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/propeller/push/instructions/polymorphic.cljc b/src/propeller/push/instructions/polymorphic.cljc index 696ef39..8849ab7 100755 --- a/src/propeller/push/instructions/polymorphic.cljc +++ b/src/propeller/push/instructions/polymorphic.cljc @@ -25,11 +25,17 @@ state (state/push-to-stack state stack top-item))))) +;; Limits the number of items that can be duplicated onto a stack at once. +;; We might want to extend to limit all the different that things may be +;; placed on a stack. +(def max-stack-items 100) + ;; Duplicates n copies of the top item (i.e leaves n copies there). Does not pop ;; its argument (since that would negate the effect of the duplication). The ;; number n is determined by the top INTEGER. For n = 0, equivalent to POP. ;; For n = 1, equivalent to NOOP. For n = 2, equivalent to DUP. Negative values -;; of n are treated as 0 +;; of n are treated as 0. The final number of items on the stack is limited to +;; max-stack-items. (def _dup_times ^{:stacks #{:integer}} (fn [stack state] @@ -38,7 +44,8 @@ (and (not= stack :integer) (not (state/empty-stack? state :integer)) (not (state/empty-stack? state stack)))) - (let [n (state/peek-stack state :integer) + (let [n (min (state/peek-stack state :integer) + (inc (- max-stack-items (state/stack-size state stack)))) popped-state (state/pop-stack state :integer) top-item (state/peek-stack popped-state stack) top-item-dup (take (- n 1) (repeat top-item))] @@ -50,12 +57,14 @@ ;; Duplicates the top n items on the stack, one time each. The number n is ;; determined by the top INTEGER. If n <= 0, no items will be duplicated. If ;; fewer than n items are on the stack, the entire stack will be duplicated. +;; The final number of items on the stack is limited to max-stack-items. (def _dup_items ^{:stacks #{:integer}} (fn [stack state] (if (state/empty-stack? state :integer) state - (let [n (state/peek-stack state :integer) + (let [n (min (state/peek-stack state :integer) + (- max-stack-items (state/stack-size state stack))) popped-state (state/pop-stack state :integer) top-items (take n (get popped-state stack))] (state/push-to-stack-many popped-state stack top-items))))) From da848522453a67d0fb0cced028cb312864461a0d Mon Sep 17 00:00:00 2001 From: Nic McPhee Date: Tue, 24 Nov 2020 16:00:03 -0600 Subject: [PATCH 3/3] Move `max-stack-items` to globals This creates a `push/utils/globals/cljc` similar to that created by @mcgirjau and moved the `max-stack-items` def to that namespace, updating `polymorphic` as necessary. --- src/propeller/push/instructions/polymorphic.cljc | 14 +++++--------- src/propeller/push/utils/globals.cljc | 11 +++++++++++ 2 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 src/propeller/push/utils/globals.cljc diff --git a/src/propeller/push/instructions/polymorphic.cljc b/src/propeller/push/instructions/polymorphic.cljc index 8849ab7..485a64a 100755 --- a/src/propeller/push/instructions/polymorphic.cljc +++ b/src/propeller/push/instructions/polymorphic.cljc @@ -5,6 +5,7 @@ (:require [propeller.utils :as utils] [propeller.push.state :as state] [propeller.push.utils.helpers :refer [make-instruction]] + [propeller.push.utils.globals :as globals] #?(:clj [propeller.push.utils.macros :refer [def-instruction generate-instructions]]))) @@ -25,17 +26,12 @@ state (state/push-to-stack state stack top-item))))) -;; Limits the number of items that can be duplicated onto a stack at once. -;; We might want to extend to limit all the different that things may be -;; placed on a stack. -(def max-stack-items 100) - ;; Duplicates n copies of the top item (i.e leaves n copies there). Does not pop ;; its argument (since that would negate the effect of the duplication). The ;; number n is determined by the top INTEGER. For n = 0, equivalent to POP. ;; For n = 1, equivalent to NOOP. For n = 2, equivalent to DUP. Negative values ;; of n are treated as 0. The final number of items on the stack is limited to -;; max-stack-items. +;; globals/max-stack-items. (def _dup_times ^{:stacks #{:integer}} (fn [stack state] @@ -45,7 +41,7 @@ (not (state/empty-stack? state :integer)) (not (state/empty-stack? state stack)))) (let [n (min (state/peek-stack state :integer) - (inc (- max-stack-items (state/stack-size state stack)))) + (inc (- globals/max-stack-items (state/stack-size state stack)))) popped-state (state/pop-stack state :integer) top-item (state/peek-stack popped-state stack) top-item-dup (take (- n 1) (repeat top-item))] @@ -57,14 +53,14 @@ ;; Duplicates the top n items on the stack, one time each. The number n is ;; determined by the top INTEGER. If n <= 0, no items will be duplicated. If ;; fewer than n items are on the stack, the entire stack will be duplicated. -;; The final number of items on the stack is limited to max-stack-items. +;; The final number of items on the stack is limited to globals/max-stack-items. (def _dup_items ^{:stacks #{:integer}} (fn [stack state] (if (state/empty-stack? state :integer) state (let [n (min (state/peek-stack state :integer) - (- max-stack-items (state/stack-size state stack))) + (- globals/max-stack-items (state/stack-size state stack))) popped-state (state/pop-stack state :integer) top-items (take n (get popped-state stack))] (state/push-to-stack-many popped-state stack top-items))))) diff --git a/src/propeller/push/utils/globals.cljc b/src/propeller/push/utils/globals.cljc new file mode 100644 index 0000000..26c37f1 --- /dev/null +++ b/src/propeller/push/utils/globals.cljc @@ -0,0 +1,11 @@ +(ns propeller.push.utils.globals) + +;; ============================================================================= +;; Values used by the Push instructions to keep the stack sizes within +;; reasonable limits. +;; ============================================================================= + +;; 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