Skip to content

Commit

Permalink
Add exception if function called with wrong arity (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
DeLaGuardo committed May 6, 2020
1 parent 78cf16c commit dd73889
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 15 deletions.
42 changes: 40 additions & 2 deletions src/axel_f/compiler.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,35 @@
{:begin begin}))))
(select-keys op-ast [::lexer/begin ::lexer/end])))

(defn min-args-count [arglist]
(reduce
(fn [acc i]
(if (= '& i)
(reduced acc)
(inc acc)))
0
arglist))

(defn max-args-count [arglist]
(reduce
(fn [acc i]
(if (= '& i)
(reduced :unbound)
(inc acc)))
0
arglist))

(defn enough-args? [arglists args]
(let [given (count args)]
(some (fn [arglist]
(let [min (min-args-count arglist)
max (max-args-count arglist)]
(and (>= given min)
(if (= :unbound max)
true
(= given (max-args-count arglist))))))
arglists)))

(defn compile-application [env {{::parser/keys [parts] :as f} ::parser/function
args ::parser/args
:as ast}]
Expand All @@ -78,8 +107,17 @@
(with-meta
(fn [ctx]
(if-let [f' (f ctx)]
(apply f' (for [x args]
(x ctx)))
(let [args (for [x args]
(x ctx))]
(if-let [arglists (:arglists (meta f'))]
(if (enough-args? arglists args)
(apply f' args)
(throw (ex-info (str "Wrong number of arguments passed to a function " (:name (meta f'))) {})))
(try
(apply f' args)
(catch #?(:clj Exception
:cljs js/Error) e
(throw (ex-info "Error during function call" {} e))))))
(throw (ex-info (str "Unknown function " (string/join "." (first (:free-variables (meta f))))) {}))))
{:free-variables (mapcat #(:free-variables (meta %)) args)
:fn-name (:free-variables (meta f))})))
Expand Down
12 changes: 7 additions & 5 deletions src/axel_f/excel/collections.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,20 @@

(def CONCAT #'CONCAT*)

(defn- walk
([f data] (walk f -1 data))
(defn WALK*
([f data] (WALK* f -1 data))
([f level data]
(cond
(= 0 level) (f data)
(sequential? data) (map (partial walk f (dec level)) data)
(map? data) (ut/map-vals (partial walk f (dec level)) data)
(sequential? data) (map (partial WALK* f (dec level)) data)
(map? data) (ut/map-vals (partial WALK* f (dec level)) data)
:else (f data))))

(def WALK #'WALK*)

(def env
{"MAP" MAP
"KEEP" FILTER
"CONCAT" CONCAT
"SORT" SORT
"walk" walk})
"walk" WALK})
8 changes: 4 additions & 4 deletions src/axel_f/excel/hash.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,28 @@
[^{:doc "String to caclulate sha384 digest"} msg]
(codecs/bytes->hex (hash/sha384 msg)))

(def sha384 sha384*)
(def sha384 #'sha384*)

(defn sha512*
"Calculates a sha-512 based digest from a String"
[^{:doc "String to caclulate sha512 digest"} msg]
(codecs/bytes->hex (hash/sha512 msg)))

(def sha512 sha512*)
(def sha512 #'sha512*)

(defn sha1*
"Calculates a sha-1 based digest from a String"
[^{:doc "String to caclulate sha1 digest"} msg]
(codecs/bytes->hex (hash/sha1 msg)))

(def sha1 sha1*)
(def sha1 #'sha1*)

(defn md5*
"Calculates a md5 based digest from a String"
[^{:doc "String to caclulate md5 digest"} msg]
(codecs/bytes->hex (hash/md5 msg)))

(def md5 md5*)
(def md5 #'md5*)

(def env
{"HASH" {"SHA256" sha256
Expand Down
10 changes: 6 additions & 4 deletions src/axel_f/excel/special_forms.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
(butlast args))]
(with-meta
(fn [ctx]
(fn [& args]
(body (if-let [args (not-empty (mapcat identity (zipmap arglist args)))]
(apply assoc ctx args)
ctx))))
(with-meta
(fn [& args]
(body (if-let [args (not-empty (mapcat identity (zipmap arglist args)))]
(apply assoc ctx args)
ctx)))
{:arglists (list arglist)}))
{:free-variables (filter (fn [[v & _]]
(not (contains? (set arglist) v)))
(:free-variables (meta body)))
Expand Down
70 changes: 70 additions & 0 deletions test/axel_f/arity_test.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
(ns axel-f.arity-test
(:require #?(:clj [clojure.test :as t]
:cljs [cljs.test :as t :include-macros true])
[axel-f.excel :as af])
#?(:clj (:import [clojure.lang ExceptionInfo])))

(defn foo-1 [_x _y & _z])

(defn foo-2 [_x _y])

(t/deftest stdlib

(t/testing "variadic args"

(let [f (af/compile "FOO(1,2,3)" {"FOO" #'foo-1})]
(t/is (nil? (f))))

(let [f (af/compile "FOO(1)" {"FOO" #'foo-1})]
(t/is
(thrown-with-msg?
ExceptionInfo
#"Wrong number of arguments passed to a function foo-1"
(f)))))

(t/testing "fixed args"

(let [f (af/compile "FOO(1)" {"FOO" #'foo-2})]
(t/is
(thrown?
ExceptionInfo
(f))))

(let [f (af/compile "FOO(1,2,3,4)" {"FOO" #'foo-2})]
(t/is
(thrown?
ExceptionInfo
(f)))))

(t/testing "lambda"

(let [f (af/compile "WITH(FOO, FN(x,y,1), FOO(1))")]
(t/is
(thrown?
ExceptionInfo
(f))))

(let [f (af/compile "WITH(FOO, FN(x,y,1), FOO(1,2,3,4))")]
(t/is
(thrown?
ExceptionInfo
(f)))))

#?(:clj
(t/testing "no meta"

(let [f (af/compile "FOO(1,2)" {"FOO" (fn [_x])})]
(t/is
(thrown-with-msg?
ExceptionInfo
#"Error during function call"
(f))))))

(t/testing "no function"

(let [f (af/compile "FOO(1,2)")]
(t/is
(thrown-with-msg?
ExceptionInfo
#"Unknown function FOO"
(f))))))

0 comments on commit dd73889

Please sign in to comment.