| author | Alan Dipert
<alan@dipert.org> 2026-11-06 20:14:07 UTC |
| committer | Alan Dipert
<alan@dipert.org> 2026-11-06 20:14:07 UTC |
| parent | d7b99f145268bbbf9ac6f7976e3fec958637d1c1 |
| .forgejo/workflows/check.yml | +43 | -0 |
| README.md | +3 | -2 |
| doc/compiler.md | +4 | -5 |
| doc/language.md | +5 | -7 |
| examples/blacktide/algorithms.clj | +2 | -3 |
| src/com/tailrecursion/blacktide.clj | +1 | -1 |
| src/com/tailrecursion/blacktide/compiler/lower.clj | +1 | -5 |
| src/com/tailrecursion/blacktide/compiler/source.clj | +0 | -2 |
| test/com/tailrecursion/blacktide/aot_test.clj | +1 | -1 |
| test/com/tailrecursion/blacktide/backend_test.clj | +3 | -3 |
| test/com/tailrecursion/blacktide/check.clj | +1 | -1 |
| test/com/tailrecursion/blacktide/generated_test.clj | +1 | -1 |
| test/com/tailrecursion/blacktide/jumps_test.clj | +9 | -9 |
| test/com/tailrecursion/blacktide/jvm_fixture.clj | +1 | -1 |
| test/com/tailrecursion/blacktide/jvm_test.clj | +4 | -4 |
| test/com/tailrecursion/blacktide/loops_test.clj | +1 | -1 |
| test/com/tailrecursion/blacktide/source_test.clj | +2 | -2 |
| test/com/tailrecursion/blacktide/subset_test.clj | +15 | -15 |
| test/com/tailrecursion/nanopass/assignment_test.clj | +6 | -10 |
| test/com/tailrecursion/nanopass/clients/assignment.clj | +0 | -1 |
diff --git a/.forgejo/workflows/check.yml b/.forgejo/workflows/check.yml new file mode 100644 index 0000000..52fe86f --- /dev/null +++ b/.forgejo/workflows/check.yml @@ -0,0 +1,43 @@ +name: Check + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + check: + runs-on: debian + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + java: ['17', '21', '25', '27'] + steps: + - uses: https://github.com/actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false + - uses: https://github.com/actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: ${{ matrix.java }} + - uses: https://data.forgejo.org/actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.m2/repository + key: maven-${{ matrix.java }}-${{ hashFiles('deps.edn') }} + - name: Install Clojure CLI + run: | + cd "$RUNNER_TEMP" + curl -fsSLO https://download.clojure.org/install/linux-install-1.12.4.1618.sh + bash linux-install-1.12.4.1618.sh --prefix "$RUNNER_TEMP/clojure" + echo "$RUNNER_TEMP/clojure/bin" >> "$GITHUB_PATH" + - name: Test + run: | + clojure -T:build compile-java + clojure -M:test + - name: Check pass types + if: matrix.java == '25' || matrix.java == '27' + run: clojure -M:check + - name: Build JAR + if: matrix.java == '25' + run: clojure -T:build jar diff --git a/README.md b/README.md index 6997247..b7d98b8 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Clojure, without mutable cells or a runtime interpreter for local state. `setq` assigns pairs sequentially and returns the last value. `psetq` evaluates all right-hand sides before assigning and returns nil. Both work in expressions. Labels belong to the outer statement sequence; jumps can leave nested statement -bodies. `(goto end)` exits with nil. +bodies. Every jump requires an explicit label. The region supports `let`, destructuring, `if`, `case`, `loop`/`recur`, calls, collections and Java interop. Ordinary macros such as `when`, `cond`, `dotimes` @@ -43,7 +43,7 @@ This is a control-flow tool; it makes no general speedup guarantee. Requires Clojure 1.12.6 and JDK 17 or later. From a neighboring checkout: ```clojure -{:deps {com.tailrecursion/blacktide {:local/root "../loophole"}}} +{:deps {com.tailrecursion/blacktide {:local/root "../blacktide"}}} ``` Run `clojure -X:deps prep` in the client project to compile the bundled JVM @@ -63,6 +63,7 @@ clojure -T:build jar # target/blacktide-0.1.0-SNAPSHOT.jar Select a JDK with `JAVA_CMD=/path/to/jdk/bin/java bin/check`. Development targets JDK 25/27; 17/21 are compatibility targets. Generators use deterministic seeds, independent models and shrinking; failures report their seed and source. +Forgejo CI tests all four JDKs, checks pass types on 25/27, and builds the JAR. See the [compiler architecture](doc/compiler.md) and [nanopass guide](doc/nanopass.md) for implementation contracts and extension guidance. diff --git a/doc/compiler.md b/doc/compiler.md index f2102e5..19b48b1 100644 --- a/doc/compiler.md +++ b/doc/compiler.md @@ -18,11 +18,10 @@ contracts. Compiler namespaces and intermediate representations are internal. `Bound → Classified → Sequential → Assigned → Cases → Collections → Prepared → Ordered → Symbolic → CFG → Machine → Code`. -The outer bindings enter as one lexical scope. `goto end` directly returns nil; -emission seals the whole result with an Object type boundary. `Native` is the -analyzed subtree language without DSL operations. The structured backend uses -`Machine → Planned → Joined → Stored → Compact → Code` and shares graph -validation with the dispatcher. +The outer bindings enter as one lexical scope; emission seals the result with an +Object type boundary. `Native` contains analyzed subtrees without DSL operations. +The structured backend uses `Machine → Planned → Joined → Stored → Compact → Code` +and shares graph validation with the dispatcher. Assignments, normalization and graph construction are pure. Locals become lexical bindings and loop parameters, never mutable cells. A generated diff --git a/doc/language.md b/doc/language.md index a107484..dfc6e12 100644 --- a/doc/language.md +++ b/doc/language.md @@ -33,12 +33,12 @@ Only bindings introduced by `loophole` are assignable. Shadowing with an ordinar Labels are simple symbols in the **outer statement sequence**, including nested transparent `do` forms after macroexpansion. They cannot appear inside `let`, `loop`, conditional arms, operands or initializers. Names are unique within the -region; `end` is reserved. +region. Labels evaluate to nil. `goto` is a statement. It can occur in `if`/`case` arms and `let`/`loop` bodies and jump out to an outer label. It is forbidden anywhere within an operand or -initializer, even a discarded expression nested in `do`. `(goto end)` returns -nil immediately. A normal final result can be obtained with a final label: +initializer, even a discarded expression nested in `do`. Every jump requires +an explicit label in the same region; execution continues after that label. ```clojure (loophole [x 0] @@ -49,8 +49,6 @@ nil immediately. A normal final result can be obtained with a final label: (goto done) (setq x 99) (label done) x) ; => 3 - -(loophole [] (goto end) :unreachable) ; => nil ``` Backward jumps rerun initializers they encounter. They cannot enter a lexical @@ -58,8 +56,8 @@ scope or bypass its initialization. These forms are errors: ```clojure (loophole [] (let [] (label inside))) -(loophole [] (+ 1 (do (goto end) 2))) -(loophole [x (goto end)] x) +(loophole [] (+ 1 (do (goto done) 2)) (label done)) +(loophole [x (goto done)] (label done) x) ``` ## Clojure subset diff --git a/examples/blacktide/algorithms.clj b/examples/blacktide/algorithms.clj index 24a03d4..e7bdc71 100644 --- a/examples/blacktide/algorithms.clj +++ b/examples/blacktide/algorithms.clj @@ -33,7 +33,7 @@ (defn decimal-prefix [^String s] (loophole [i 0 sign 1 value 0N start 0] - (when (empty? s) (goto end)) + (when (empty? s) (goto done)) (case (.charAt s 0) \- (setq sign -1 i 1) \+ (setq i 1) @@ -47,5 +47,4 @@ i (inc i))) (goto digit) (label done) - (when (= i start) (goto end)) - [(* sign value) i])) + (when (< start i) [(* sign value) i]))) diff --git a/src/com/tailrecursion/blacktide.clj b/src/com/tailrecursion/blacktide.clj index ab04ce0..b3e558a 100644 --- a/src/com/tailrecursion/blacktide.clj +++ b/src/com/tailrecursion/blacktide.clj @@ -11,7 +11,7 @@ (errors/site {:form &form :file *file* :line nil :column nil} &form))) (defmacro goto - "Jump from a statement to an outer label; (goto end) returns nil." + "Jump from a statement to an explicit outer label." [& _] (errors/fail :outside-region "goto is only valid inside loophole" {} (errors/site {:form &form :file *file* :line nil :column nil} &form))) diff --git a/src/com/tailrecursion/blacktide/compiler/lower.clj b/src/com/tailrecursion/blacktide/compiler/lower.clj index 1d812ea..e889868 100644 --- a/src/com/tailrecursion/blacktide/compiler/lower.clj +++ b/src/com/tailrecursion/blacktide/compiler/lower.clj @@ -122,11 +122,7 @@ builder (assoc builder :labels (assoc (:labels builder) key label)) builder (seal builder {:op :jump :target label})] [(start builder label) {:op :literal :value nil}])) - :goto (let [builder - (if (= 'end (:name node)) - (seal builder {:op :return :value {:op :literal :value nil}}) - (seal builder {:op :transfer :name (:name node) - :site (:site expr)})) + :goto (let [builder (seal builder {:op :transfer :name (:name node) :site (:site expr)}) [builder next] (fresh-block builder)] [(start builder next) {:op :literal :value nil}]) :do (let [[builder values] (lower-forms builder (:forms node))] diff --git a/src/com/tailrecursion/blacktide/compiler/source.clj b/src/com/tailrecursion/blacktide/compiler/source.clj index f7bebcc..53ea460 100644 --- a/src/com/tailrecursion/blacktide/compiler/source.clj +++ b/src/com/tailrecursion/blacktide/compiler/source.clj @@ -313,8 +313,6 @@ (when-not (and (:statement? ctx) (:transfer? ctx) (or (= kind :goto) (:outer? ctx))) (errors/fail :marker-position "Labels require the outer body; goto requires a statement outside operands and initializers" {:operator kind :label name} (:site ctx))) - (when (and (= kind :label) (= name 'end)) - (errors/fail :reserved-label "end is reserved" {:label name} (:site ctx))) (expression ctx {:hint nil :var-meta {}} (if (= kind :label) {:op :label :name name} {:op :goto :name name})))) diff --git a/test/com/tailrecursion/blacktide/aot_test.clj b/test/com/tailrecursion/blacktide/aot_test.clj index 445a334..eac31f7 100644 --- a/test/com/tailrecursion/blacktide/aot_test.clj +++ b/test/com/tailrecursion/blacktide/aot_test.clj @@ -13,7 +13,7 @@ {:exit (.waitFor process) :output output})) (deftest fresh-aot-and-source-free-use - (let [directory (.toFile (java.nio.file.Files/createTempDirectory "loophole-aot-" (make-array java.nio.file.attribute.FileAttribute 0))) + (let [directory (.toFile (java.nio.file.Files/createTempDirectory "blacktide-aot-" (make-array java.nio.file.attribute.FileAttribute 0))) classpath (System/getProperty "java.class.path") dependencies (filter #(str/ends-with? % ".jar") (str/split classpath (re-pattern java.io.File/pathSeparator))) compiled (subprocess classpath `(binding [*compile-path* ~(.getPath directory)] diff --git a/test/com/tailrecursion/blacktide/backend_test.clj b/test/com/tailrecursion/blacktide/backend_test.clj index 244271f..66ed081 100644 --- a/test/com/tailrecursion/blacktide/backend_test.clj +++ b/test/com/tailrecursion/blacktide/backend_test.clj @@ -50,7 +50,7 @@ (list 'quote (.getJavaClass ^clojure.lang.Compiler$LocalBinding (get &env name)))) (deftest result-type-boundary - (doseq [body '[1 2.5 nil false "text" (loop [] 1) (goto end)] + (doseq [body '[1 2.5 nil false "text" (loop [] 1) (do (goto end) :dead (label end) 42)] mode [:default :dispatcher]] (let [form (list 'let ['result (list 'loophole [] body)] '(local-class result))] (is (= Object ((compile-program form mode) nil)) (pr-str [body mode]))))) @@ -195,12 +195,12 @@ (deftest structured-code-preserves-native-validation (doseq [invalid '[(Math/abs 1 2 3) (String. 1 2 3 4 5)]] (let [form (list 'loophole '[i 0] '(label again) '(setq i (inc i)) - '(when (< i 2) (goto again)) '(goto end) invalid)] + '(when (< i 2) (goto again)) '(goto done) invalid '(label done))] (doseq [mode [:default :dispatcher]] (is (thrown? clojure.lang.Compiler$CompilerException (compile-program form mode)))))) (let [form '(loophole [i 0] (label again) (setq i (inc i)) (when (< i 2) (goto again)) - (goto end) (swap! events conj :dead)) + (goto done) (swap! events conj :dead) (label done)) events (atom [])] (is (= :validation (:reason (selection form)))) (is (nil? ((compile-program form :default) events))) diff --git a/test/com/tailrecursion/blacktide/check.clj b/test/com/tailrecursion/blacktide/check.clj index 9d3b0cf..50f3b29 100644 --- a/test/com/tailrecursion/blacktide/check.clj +++ b/test/com/tailrecursion/blacktide/check.clj @@ -58,7 +58,7 @@ (deftest native-and-recur-boundaries (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Native-Node {:op :setq :pairs []} false) (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Native-Node - {:op :goto :name 'end} false) + {:op :goto :name 'done} false) (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Ordered-Node {:op :recur :target {:id 'loop :bindings []} :args []} true) (probes/accepts? 'com.tailrecursion.blacktide.compiler.source-languages/Ordered-Node diff --git a/test/com/tailrecursion/blacktide/generated_test.clj b/test/com/tailrecursion/blacktide/generated_test.clj index a4a2756..b92ce7c 100644 --- a/test/com/tailrecursion/blacktide/generated_test.clj +++ b/test/com/tailrecursion/blacktide/generated_test.clj @@ -80,7 +80,7 @@ form `(loophole [~(local 'i) 0 ~(local 'x) ~(initial x) ~(local 'y) ~(initial y)] ~@forms (label ~'done) ~result)] (if renamed? - (let [renames '{i counter__17 x value__93 y value__94 again start__51 done finish__88}] + (let [renames '{i counter__17 x value__93 y value__94 again start__51 done end}] (list 'let ['unused :harmless] (walk/postwalk #(if (symbol? %) (with-meta (get renames % %) (meta %)) %) form))) form))) diff --git a/test/com/tailrecursion/blacktide/jumps_test.clj b/test/com/tailrecursion/blacktide/jumps_test.clj index 60e04c8..fedcb7d 100644 --- a/test/com/tailrecursion/blacktide/jumps_test.clj +++ b/test/com/tailrecursion/blacktide/jumps_test.clj @@ -11,8 +11,8 @@ (defn record! [events tag value] (swap! events conj tag) value) (deftest basic-and-conditional-transfers - (is (= 1 (loophole [x 1] (goto done) (setq x 9) (label done) x))) - (is (nil? (loophole [] (goto end) :unreachable))) + (is (= 1 (loophole [x 1] (goto end) (setq x 9) (label end) x))) + (is (nil? (loophole [] (goto done) :unreachable (label done)))) (is (nil? (loophole [] (label first) (label last)))) (is (= 0 (loophole [n 3] (label again) (setq n (step n)) (when (more? n) (goto again)) n)))) @@ -27,20 +27,20 @@ (deftest invalid-transfers-even-in-dead-source (doseq [[body kind] [['(goto missing) :undefined-label] + ['(goto end) :undefined-label] ['(do (label x) (label x)) :duplicate-label] - ['(label end) :reserved-label] ['(label :keyword) :invalid-marker] ['(goto qualified/name) :invalid-marker] ['(vector (label x)) :marker-position] ['(let [] (label x)) :marker-position] ['(if true (label x) nil) :marker-position] ['(case 0 0 (label x) nil) :marker-position] - ['(vector (do (goto end) 1)) :marker-position] - ['(let [x (do (goto end) 1)] x) :marker-position] - ['(setq x (do (goto end) 1)) :marker-position] + ['(vector (do (goto done) 1)) :marker-position] + ['(let [x (do (goto done) 1)] x) :marker-position] + ['(setq x (do (goto done) 1)) :marker-position] ['(loophole [] 1) :unsupported-form]] - prefix [[] ['(goto end)]]] - (is (= kind (:kind (source/rejection (list* 'loophole '[x 0] (concat prefix [body]))))) + prefix [[] ['(goto done)]]] + (is (= kind (:kind (source/rejection (list* 'loophole '[x 0] (concat prefix [body '(label done)]))))) (pr-str [prefix body])))) (defn sum-through [input] @@ -105,7 +105,7 @@ :yes (.nextInt random 3) :no (.nextInt random 3)))) budget (.nextInt random 14) value (- (.nextInt random 41) 20) expected (reference-machine table budget value)] - (doseq [[labels wrapped?] [['[a b c] false] ['[renamed-a renamed-b renamed-c] true]]] + (doseq [[labels wrapped?] [['[end b c] false] ['[renamed-a renamed-b renamed-c] true]]] (let [actual (execute-machine table budget value labels wrapped?)] (is (= expected actual) (when (not= expected actual) diff --git a/test/com/tailrecursion/blacktide/jvm_fixture.clj b/test/com/tailrecursion/blacktide/jvm_fixture.clj index d96a53e..874a34d 100644 --- a/test/com/tailrecursion/blacktide/jvm_fixture.clj +++ b/test/com/tailrecursion/blacktide/jvm_fixture.clj @@ -3,7 +3,7 @@ (:require [clojure.java.io :as io])) (def fixture-class - (let [directory (.toFile (java.nio.file.Files/createTempDirectory "loophole-jvm-fixture-" + (let [directory (.toFile (java.nio.file.Files/createTempDirectory "blacktide-jvm-fixture-" (make-array java.nio.file.attribute.FileAttribute 0))) source (io/file directory "HostFixture.java") compiler (javax.tools.ToolProvider/getSystemJavaCompiler)] diff --git a/test/com/tailrecursion/blacktide/jvm_test.clj b/test/com/tailrecursion/blacktide/jvm_test.clj index 8c6fba0..390faa3 100644 --- a/test/com/tailrecursion/blacktide/jvm_test.clj +++ b/test/com/tailrecursion/blacktide/jvm_test.clj @@ -221,13 +221,13 @@ wrap [identity #(list 'do %)]] (let [body (wrap form) expected (outcome body) - actual (outcome (list 'loophole [] '(goto end) body))] + actual (outcome (list 'loophole [] '(goto done) body '(label done)))] (is (:error expected) (pr-str {:form body :native expected})) (is (= (:error expected) (:error actual)) (pr-str {:form body :native expected :actual actual})))) ;; Instance reflection reports a missing overload only when executed. - (is (nil? (loophole [] (goto end) (.substring "abc" 1 2 3)))) + (is (nil? (loophole [] (goto done) (.substring "abc" 1 2 3) (label done)))) (let [events (atom [])] - (is (nil? (loophole [] (goto end) + (is (nil? (loophole [] (goto done) (swap! events conj :expression) - (identity (swap! events conj :after))))) + (identity (swap! events conj :after)) (label done)))) (is (empty? @events)))) diff --git a/test/com/tailrecursion/blacktide/loops_test.clj b/test/com/tailrecursion/blacktide/loops_test.clj index 812187e..9c77a66 100644 --- a/test/com/tailrecursion/blacktide/loops_test.clj +++ b/test/com/tailrecursion/blacktide/loops_test.clj @@ -124,7 +124,7 @@ ['(loophole [x nil] (loop [n 1] (setq x n) (recur (short n)))) :invalid-recur-type] ['(loophole [] (goto body) (loop [] (label body))) :marker-position] ['(loophole [] (loop [] (goto other)) (loop [] (label other))) :marker-position] - ['(loophole [] (goto end) (loop [] (recur 1))) :recur-arity]]] + ['(loophole [] (goto done) (loop [] (recur 1)) (label done)) :recur-arity]]] (is (= kind (:kind (source/rejection form))) (pr-str form)))) (deftest illegal-primitive-recur-conversions diff --git a/test/com/tailrecursion/blacktide/source_test.clj b/test/com/tailrecursion/blacktide/source_test.clj index 2c64535..5869e03 100644 --- a/test/com/tailrecursion/blacktide/source_test.clj +++ b/test/com/tailrecursion/blacktide/source_test.clj @@ -168,8 +168,8 @@ (deftest diagnostic-context-survives-expansion-and-native-boundaries (doseq [body '[(case)] - prefix [[] ['(goto end)]]] - (let [form (with-meta (list* 'loophole [] (concat prefix [body])) {:line 123 :column 9}) + prefix [[] ['(goto done)]]] + (let [form (with-meta (list* 'loophole [] (concat prefix [body '(label done)])) {:line 123 :column 9}) error (binding [*file* "diagnostic-example.clj"] (rejection form))] (is (= :invalid-case (:kind error))) (is (= '(case) (:form error))) diff --git a/test/com/tailrecursion/blacktide/subset_test.clj b/test/com/tailrecursion/blacktide/subset_test.clj index 12719e3..6b4db4a 100644 --- a/test/com/tailrecursion/blacktide/subset_test.clj +++ b/test/com/tailrecursion/blacktide/subset_test.clj @@ -16,27 +16,27 @@ (binding [*print-length* 1] 2) (locking (Object.) 1) (with-open [r (java.io.StringReader. "")] 1) (with-redefs [identity vector] 1) (com.tailrecursion.blacktide.subset-test/hidden-function)] - prefix [[] ['(goto end)]]] + prefix [[] ['(goto done)]]] (is (= :unsupported-form - (:kind (source/rejection (list* 'loophole '[x 0] (concat prefix [body]))))) + (:kind (source/rejection (list* 'loophole '[x 0] (concat prefix [body '(label done)]))))) (pr-str [prefix body])))) (deftest operands-cannot-hide-transfers - (doseq [body '[(identity (do (goto end) 1)) - ((do (goto end) identity) 1) - (if (do (goto end) true) 1 2) - (case (do (goto end) 1) 1 2 3) - (let [a (do (goto end) 1)] a) - (loop [a (do (goto end) 1)] a) - (loop [a 0] (recur (do (goto end) 1))) - (setq x (case 1 1 (goto end) 2)) - [(let [] (goto end) 1)] - (identity (loop [] (goto end))) - ^{:note (do (goto end) 1)} [1] + (doseq [body '[(identity (do (goto done) 1)) + ((do (goto done) identity) 1) + (if (do (goto done) true) 1 2) + (case (do (goto done) 1) 1 2 3) + (let [a (do (goto done) 1)] a) + (loop [a (do (goto done) 1)] a) + (loop [a 0] (recur (do (goto done) 1))) + (setq x (case 1 1 (goto done) 2)) + [(let [] (goto done) 1)] + (identity (loop [] (goto done))) + ^{:note (do (goto done) 1)} [1] (let [] (com.tailrecursion.blacktide.subset-test/hidden-label))]] - (is (= :marker-position (:kind (source/rejection (list 'loophole '[x 0] body)))) (pr-str body))) + (is (= :marker-position (:kind (source/rejection (list 'loophole '[x 0] body '(label done))))) (pr-str body))) (is (= :ok (loophole [] (do (label a) (do (goto b))) :wrong (label b) :ok))) - (is (= '(fn [] (goto end)) (loophole [] '(fn [] (goto end)))))) + (is (= '(fn [] (goto missing)) (loophole [] '(fn [] (goto missing)))))) (deftest caller-functions-and-dynamic-extent (let [events (atom []) helper (fn [x] (try (inc x) (finally (swap! events conj :helper))))] diff --git a/test/com/tailrecursion/nanopass/assignment_test.clj b/test/com/tailrecursion/nanopass/assignment_test.clj index 6fa625c..e7d4ab1 100644 --- a/test/com/tailrecursion/nanopass/assignment_test.clj +++ b/test/com/tailrecursion/nanopass/assignment_test.clj @@ -27,7 +27,6 @@ (run (if test (:then form) (:else form)) state)) :event (let [[value state] (run (:value form) state)] [value (update state :trace conj [(:tag form) value])]) - :exit (throw (ex-info "Region exit" {:status :exit :state state})) :throw (throw (ex-info "Source exception" {:status :throw :tag (:tag form) :state state})) ;; Source assignments define their semantics directly, independent of ;; the pass's generated binding tree and the output's write primitive. @@ -94,18 +93,15 @@ (is (= expected (execute (lower/lower source) {'x 1 'y 2})))))))) (deftest abandoned-writes - (doseq [op [:setq :psetq] - transfer [{:op :exit} {:op :throw :tag :failed}] - nested? [false true]] + (doseq [op [:setq :psetq] nested? [false true]] (let [source (batch op 'x (event :first (lit 10)) 'y (sequence-of (if nested? (batch :setq 'x (lit 9)) (lit nil)) - transfer)) - expected (cond-> {:status (if (= :exit (:op transfer)) :exit :throw) - :vars {'x (cond nested? 9 (= op :setq) 10 :else 1) 'y 2} - :trace [[:first 10]]} - (= :throw (:op transfer)) (assoc :tag :failed))] - (testing (pr-str [op transfer nested?]) + {:op :throw :tag :failed})) + expected {:status :throw :tag :failed + :vars {'x (cond nested? 9 (= op :setq) 10 :else 1) 'y 2} + :trace [[:first 10]]}] + (testing (pr-str [op nested?]) (is (= expected (execute source {'x 1 'y 2}))) (is (= expected (execute (lower/lower source) {'x 1 'y 2}))))))) diff --git a/test/com/tailrecursion/nanopass/clients/assignment.clj b/test/com/tailrecursion/nanopass/clients/assignment.clj index c6f13d1..1090a9f 100644 --- a/test/com/tailrecursion/nanopass/clients/assignment.clj +++ b/test/com/tailrecursion/nanopass/clients/assignment.clj @@ -7,7 +7,6 @@ (np/deflanguage Source {Expr (t/U (t/HMap :mandatory {:op ':const :value t/Any} :complete? true) (t/HMap :mandatory {:op ':read :binding t/Sym} :complete? true) - (t/HMap :mandatory {:op ':exit} :complete? true) (t/HMap :mandatory {:op ':throw :tag t/Keyword} :complete? true) (t/HMap :mandatory {:op ':add :left Expr :right Expr} :complete? true) (t/HMap :mandatory {:op ':do :forms (t/Vec Expr)} :complete? true)