~technomancy/fennel

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
3 2

[PATCH v2 antifennel] Flatten associative operations

Ambrose Bonnaire-Sergeant <abonnairesergeant@gmail.com>
Details
Message ID
<20230314195906.60548-1-ambrose@ambrosebs.com>
DKIM signature
missing
Download raw message
Patch: +92 -7
This nicely cleaned up a conversion I'm working with
that has bor's nested 6-deep.

Fixes v1 of this patch by only flattening operations
with 2 or more arguments.
---
 anticompiler.fnl  | 37 ++++++++++++++++++++++++++++++++-----
 test.lua          | 19 +++++++++++++++++++
 test_expected.fnl | 43 +++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 92 insertions(+), 7 deletions(-)

diff --git a/anticompiler.fnl b/anticompiler.fnl
index d7297b1..495851d 100644
--- a/anticompiler.fnl
+++ b/anticompiler.fnl
@@ -1,7 +1,7 @@
;; The name of this module is intended as a joke; this is in fact a compiler,
;; not an "anticompiler" even tho it goes in reverse from the fennel compiler
;; see http://nonadventures.com/2013/07/27/you-say-you-want-a-devolution/
(local {: list : mangle : sym : sym? : view : sequence : multi-sym? : sym-char?}
(local {: list : mangle : sym : sym? : view : sequence : multi-sym? : sym-char? : list?}
       (require :fennel))
(local unpack (or table.unpack _G.unpack))

@@ -12,6 +12,13 @@
      (table.insert out (f v (and with-last? (= i len)))))
    out))

(fn mapcat [tbl f]
  (let [out []]
    (each [_ v (ipairs tbl)]
      (each [_ v (ipairs (f v))]
        (table.insert out v)))
    out))

(fn distinct [tbl]
  (let [seen {}]
    (icollect [_ x (ipairs tbl)]
@@ -114,12 +121,32 @@
        (list (sym :lua)
              (.. "return " (table.concat (map args view) ", "))))))

(fn flatten-associative [associative-op-sym frm]
  (if (and (list? frm)
           (<= 3 (length frm))
           (= (. frm 1) associative-op-sym))
    (icollect [i frm (ipairs frm)]
      (when (not= 1 i)
        frm))
    [frm]))

(local associative-operators
  (collect [_ op (pairs [:band :bor :bxor :+ :*])]
    op op))
(fn binary [compile scope {: left : right : operator} ast]
  (let [operators {:== := "~=" :not= "#" :length "~" :bxor
                   :<< :lshift :>> :rshift :& :band :| :bor}]
    (list (sym (or (. operators operator) operator))
          (compile scope left)
          (compile scope right))))
                   :<< :lshift :>> :rshift :& :band :| :bor}
        op-str (or (. operators operator) operator)
        op-sym (sym op-str)
        compile (partial compile scope)]
    (if (. associative-operators op-str)
      (list op-sym
            (unpack
              (mapcat [left right]
                      #(flatten-associative op-sym (compile $)))))
      (list op-sym
            (compile left)
            (compile right)))))

(fn unary [compile scope {: argument : operator} ast]
  (let [operators {"~" :bnot}]
diff --git a/test.lua b/test.lua
index 718c456..b67a59b 100644
--- a/test.lua
+++ b/test.lua
@@ -120,7 +120,26 @@ assert ((50 >> 2) >> 1 == 6)
assert (50 >> (2 >> 1) == 25)
assert ((59 >> 2) << 127 == 0)
assert (59 >> (2 << 127) == 59)
assert (50 << 2 << 1 == 400)
assert ((50 << 2) << 1 == 400)
assert (50 << (2 << 1) == 800)
assert ((~ 1) == -2)
assert ((1 + (~ 1)) == -1)
assert (1 | 2 | 3 == 3)
assert (1 | (2 | 3) == 3)
assert (1 | 2 | 3 | 4 == 7)
assert (1 | (2 | 3 | 4) == 7)
assert (1 + 2 + 3 == 6)
assert (1 + 2 + 3 + 4 == 10)
assert (1 + (2 + 3) + 4 == 10)
assert (1 * 2 * 3 == 6)
assert (1 * 2 * 3 * 4 == 24)
assert (1 * (2 * 3) * 4 == 24)
assert (1 ~ 2 ~ 3 == 0)
assert (1 ~ 2 ~ 3 ~ 4 == 4)
assert (1 ~ (2 ~ 3) ~ 4 == 4)
assert ((1 ~ 2) & (3 ~ 4) == (1 & 3) ~ (1 & 4) ~ (2 & 3) ~ (2 & 4))
assert (16 / 4 / 4 == 1)
assert (16 / (4 / 4) == 16)

return (f123("path") or {"a", "b", "c"}).mode
diff --git a/test_expected.fnl b/test_expected.fnl
index b707e53..6383d40 100644
--- a/test_expected.fnl
+++ b/test_expected.fnl
@@ -35,8 +35,7 @@

(fn letter []
  (let [x 19
        y 20]
    (+ x y)))
        y 20] (+ x y)))

(noprint ((fn [] (let [x 1] x))))

@@ -123,8 +122,48 @@

(assert (= (rshift 59 (lshift 2 127)) 59))

(assert (= (lshift (lshift 50 2) 1) 400))

(assert (= (lshift (lshift 50 2) 1) 400))

(assert (= (lshift 50 (lshift 2 1)) 800))

(assert (= (bnot 1) (- 2)))

(assert (= (+ 1 (bnot 1)) (- 1)))

(assert (= (bor 1 2 3) 3))

(assert (= (bor 1 2 3) 3))

(assert (= (bor 1 2 3 4) 7))

(assert (= (bor 1 2 3 4) 7))

(assert (= (+ 1 2 3) 6))

(assert (= (+ 1 2 3 4) 10))

(assert (= (+ 1 2 3 4) 10))

(assert (= (* 1 2 3) 6))

(assert (= (* 1 2 3 4) 24))

(assert (= (* 1 2 3 4) 24))

(assert (= (bxor 1 2 3) 0))

(assert (= (bxor 1 2 3 4) 4))

(assert (= (bxor 1 2 3 4) 4))

(assert (= (band (bxor 1 2) (bxor 3 4))
           (bxor (band 1 3) (band 1 4) (band 2 3) (band 2 4))))

(assert (= (/ (/ 16 4) 4) 1))

(assert (= (/ 16 (/ 4 4)) 16))

(. (or (f123 :path) [:a :b :c]) :mode)

-- 
2.37.1 (Apple Git-137.1)
Details
Message ID
<87h6uma9pc.fsf@hagelb.org>
In-Reply-To
<20230314195906.60548-1-ambrose@ambrosebs.com> (view parent)
DKIM signature
missing
Download raw message
Ambrose Bonnaire-Sergeant <abonnairesergeant@gmail.com> writes:

> This nicely cleaned up a conversion I'm working with
> that has bor's nested 6-deep.

Thanks! Trying the tests here has made me realize that bnot doesn't work
with the --use-bit-lib flag, so I'm tracking that here:

  https://todo.sr.ht/~technomancy/fennel/156

But that's unrelated to the issue at hand, so I'll just go ahead and
apply both your patches. (this one and the line number one).

> +(fn flatten-associative [associative-op-sym frm]
> +  (if (and (list? frm)
> +           (<= 3 (length frm))
> +           (= (. frm 1) associative-op-sym))
> +    (icollect [i frm (ipairs frm)]
> +      (when (not= 1 i)
> +        frm))
> +    [frm]))

This function can actually be written more succinctly as a pattern match:

    (fn flatten-associative [op-sym form]
      (match (list? form)
        [op-sym a b &as op-call] (doto op-call (table.remove 1))
        _ [form]))

This works because an idiosyncrasy in Fennel where the type predicates
return their argument rather than true, and by adding a and b to the
pattern, we ensure that the length is at least 3, because they're
guaranteed to not be nil.

I wonder if we can use this same logic to make it so that t[k][k2]
compiles to a single . call instead of (. (. t k) k2).

It's not exactly the same thing since they're not operators, but it's
pretty similar. Maybe I'll take a look at this later if no one beats me
to it.

-Phil
Details
Message ID
<87edpqa9b0.fsf@hagelb.org>
In-Reply-To
<87h6uma9pc.fsf@hagelb.org> (view parent)
DKIM signature
missing
Download raw message
> I wonder if we can use this same logic to make it so that t[k][k2]
> compiles to a single . call instead of (. (. t k) k2).
>
> It's not exactly the same thing since they're not operators, but it's
> pretty similar. Maybe I'll take a look at this later if no one beats me
> to it.

Never mind; figured it out. =)

https://git.sr.ht/~technomancy/antifennel/commit/31d79f28502b34fd7bbce957d391cfed15e0e395

-Phil
Details
Message ID
<7-0A-2nFbLK_h7Xzi1n3mxQTx0mIZaKYo_2uwUXUhCwXl2BGKX-W7EM6_qMHF_R39fJZkPFyDhDx-2slibufYRtbHeG7ErtTivX6ya0BD90=@ambrosebs.com>
In-Reply-To
<87edpqa9b0.fsf@hagelb.org> (view parent)
DKIM signature
missing
Download raw message
> This works because an idiosyncrasy in Fennel where the type predicates > return their argument rather than true

Ah! I noticed the same useful behavior with Lua's `assert`​.

> Never mind; figured it out. =)

Nice!

Ambrose
Reply to thread Export thread (mbox)