~technomancy/fennel

compiler: allow strings at call position v1 APPLIED

Andrey Listopadov: 1
 compiler: allow strings at call position

 2 files changed, 13 insertions(+), 2 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~technomancy/fennel/patches/22411/mbox | git am -3
Learn more about email & git
View this thread in the archives

[PATCH] compiler: allow strings at call position Export this patch

Strings can possibly have a __call metamethod, and it's actually
possible to call strings in Lua with the following notation:
`("str")(...)`.  This patch adds a check if AST element at call
position is a string, and if it is, it is wrapped in parentheses to
ensure correct Lua syntax.
I wonder if we can wrap every literal into parentheses, as
something like this is valid lua:

local function foo(bar) return bar end
(foo)(42)

Also if __call metamethod for strings is not restored in the test, the
test-empty-values test breaks:

    ./test/luaunit.lua:2893: Expected an error when calling function
    but no error generated
---
 src/fennel/compiler.fnl | 8 ++++++--
 test/core.fnl           | 7 +++++++
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/fennel/compiler.fnl b/src/fennel/compiler.fnl
index 3826f12..023d63b 100644
--- a/src/fennel/compiler.fnl
+++ b/src/fennel/compiler.fnl
@@ -441,7 +441,8 @@ if opts contains the nval option."
(fn compile-function-call [ast scope parent opts compile1 len]
  (let [fargs [] ; regular function call
        fcallee (. (compile1 (. ast 1) scope parent {:nval 1}) 1)]
    (assert-compile (not= fcallee.type :literal)
    (assert-compile (or (= :string (type (. ast 1))) ; strings can have __call metamethod
                        (not= fcallee.type :literal))
                    (.. "cannot call literal value " (tostring (. ast 1))) ast)
    (for [i 2 len]
      (let [subexprs (compile1 (. ast i) scope parent
@@ -453,7 +454,10 @@ if opts contains the nval option."
              (table.insert fargs (. subexprs j)))
            ;; Emit sub expression only for side effects
            (keep-side-effects subexprs parent 2 (. ast i)))))
    (let [call (string.format "%s(%s)" (tostring fcallee) (exprs1 fargs))]
    (let [pat (if (= :string (type (. ast 1)))
                  "(%s)(%s)"  ; ("a")() is valid Lua call, "a"() isn't
                  "%s(%s)")   ; regular literal call
          call (string.format pat (tostring fcallee) (exprs1 fargs))]
      (handle-compile-opts [(utils.expr call :statement)] parent opts ast))))

(fn compile-call [ast scope parent opts compile1]
diff --git a/test/core.fnl b/test/core.fnl
index ceeac7e..1ff4471 100644
--- a/test/core.fnl
+++ b/test/core.fnl
@@ -109,6 +109,13 @@

               ;; many args
               "((fn f [a sin cos radC cx cy x y limit dis] sin) 8 529)" 529

               ;; string call thru metamethod
               "(tset (getmetatable ::) :__call (fn [s t] (. t s)))
                (let [res (:answer {:answer 42})]
                  ; Breaks test-empty-values test if not restored. Maybe check?
                  (tset (getmetatable ::) :__call nil)
                  res)" 42
               }]
    (each [code expected (pairs cases)]
      (l.assertEquals (fennel.eval code {:correlate true}) expected code))))
-- 
2.31.1
Andrey Listopadov <andreyorst@gmail.com> writes: