【问题标题】:clojure.spec custom generator for Java objectsJava 对象的 clojure.spec 自定义生成器
【发布时间】:2017-05-01 19:05:57
【问题描述】:

我刚刚在 clojure.spec 上看到了 Rich's talks 之一,我真的很想在我的项目中试一试。我正在编写一系列使用 eclipse CDT library 解析 C 代码的工具,我想指定我的函数接受和发出 AST 对象。

我认为可以为一个函数编写一个非常基本的规范,该函数采用 AST 的根并发出所有树的叶子,如下所示:

(import '(org.eclipse.cdt.core.dom.ast IASTNode))
(require '[clojure.spec :as s])

(defn ast-node? [node] (instance? IASTNode node))
(s/def ::ast-node ast-node?)
(s/fdef leaves :args ::ast-node :ret (s/coll-of ::ast-node))

但是,当我尝试执行代码 (s/exercise leaves) 时,我得到一个错误:

Unable to construct gen at: [] for:  
xxx.x$leaves@xxx  
#:clojure.spec{:path [], :form #function[xxx.xxx/leaves], :failure :no-gen}

如何为 Java 对象编写自定义生成器以完全规范和运行我的代码?

【问题讨论】:

    标签: clojure clojure.spec


    【解决方案1】:

    您可以使用s/with-gen 将自定义生成器附加到规范。您需要编写一个生成器来生成您需要的所有节点变体。您可能会发现为每个节点类型编写一个生成器然后将它们组合起来更容易,或者使用 s/or 或可能使用类似 s/multi-spec 的东西来代替(这将使它对扩展开放)。

    编写生成 Java 对象的生成器的示例如下:

    (s/def ::date 
      (s/with-gen #(instance? java.util.Date %)
        (fn [] (gen/fmap #(java.util.Date. %) (s/gen pos-int?)))))
    

    fmap 接受一个函数并将其应用于您提供给它的生成器的每个结果。如果您的 Java 对象的构造函数采用多个值,则可以使用源生成器,例如 (s/gen (s/tuple int? string? int?))

    【讨论】:

      【解决方案2】:

      为了完整起见,这是我在应用 Alex 的回答来指定“LiteralExpression”AST 节点后的代码:

      (ns atom-finder.ast-spec
        (:import [org.eclipse.cdt.internal.core.dom.parser.cpp CPPASTLiteralExpression])
        (:require [clojure.spec :as s]
                  [clojure.spec.gen :as gen]))
      
      (def gen-literal-expression-args
       (gen/one-of
        [
         (gen/tuple (s/gen #{CPPASTLiteralExpression/lk_char_constant})
                    (gen/char-ascii))
         (gen/tuple (s/gen #{CPPASTLiteralExpression/lk_float_constant})
                    (gen/double))
         (gen/tuple (s/gen #{CPPASTLiteralExpression/lk_integer_constant})
                    (s/gen (s/int-in -2147483648 2147483647)))
         (gen/tuple (s/gen #{CPPASTLiteralExpression/lk_string_literal})
                    (gen/string))]))
      
      (def gen-literal-expression
        (gen/fmap
         (fn [[type val]]
           (CPPASTLiteralExpression. type (.toCharArray (str val))))
         gen-literal-expression-args))
      
      (s/def ::literal-expression
        (s/with-gen
          (partial instance? CPPASTLiteralExpression)
          (fn [] gen-literal-expression)))
      
      (s/exercise :atom-finder.ast-spec/literal-expression 10
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-12-12
        • 1970-01-01
        • 1970-01-01
        • 2018-08-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多