Skip to content

Instantly share code, notes, and snippets.

@mankyKitty
Last active March 29, 2016 21:59
Show Gist options
  • Save mankyKitty/9519790 to your computer and use it in GitHub Desktop.
Save mankyKitty/9519790 to your computer and use it in GitHub Desktop.
LFE Type Signature Scanner - Takes our LFE type syntax and makes it into a data structure to be munged into Dialyzer specs.
(defmodule lfe_type_scan
(export all))
(defun to-type-fun (t)
"This function was taking the type atom 'atom and smooshing it into
\"atom()\" but this doesn't play well with how I've structured the fold
so I've left it for now whilst I tried other things."
(list t)) ;; Until further notice
(defmacro with-spec args
"This is the function spec macro for building the definition of the
function into a erlang tuple data structure"
`(tuple 'spec
(tuple ;; function argument types
(parse-type-structures (: lists takewhile (lambda (i) (=/= i '->)) (list ,@args)))
(tuple ;; return type(s)
(parse-type-structures (cdr (: lists dropwhile (lambda (i) (=/= i '->)) (list ,@args))))))))
(defun parse-type (t defs)
"Determine the base type for this new type. It could either be a new
name for an existing type or it could be a name for a polymorphic type
representation such as a (list 'int) or it could be a function in
which case the whole thing has to be check again using the same
technique."
(++ defs
(case t
;; Base case is a single atom for a renamed type
(ts (when (is_atom ts)) (to-type-fun ts))
;; Second case is function type. Either way, handle this in
;; the function spec def function, so we're consistent like a
;; boss. Additionally it must be checked prior to handling
;; parameterised types.
((cons f def) (when (is_atom f) (=:= f 'fun) (is_list def))
(with-spec def)) ;; <--- this needs to be unquoted and
;; evaluated, since it's another macro... there must be a
;; better way to do this, and it probably doesn't involve
;; macros. :P
;; Final case is a parameterised type such as (list 'int) or
;; (tuple 'int)
(ts (when (is_list ts)) (list '"[" (parse-type-structures ts) '"]"))
(ts (when (is_tuple ts)) (list '"{" (parse-type-structures (tuple_to_list ts)) '"}")))))
(defun parse-type-structures (ts)
"Given a list of types, parse the list from our expected syntax to
an erlang data structure"
(: lists foldl (lambda (t acc)
(parse-type t acc)) '() ts))
(defmacro deftype args
"Create a module type definition."
`(tuple 'type (tuple
;; Type name provided as an atom
(to-type-fun (car (list ,@args)))
;; Expect everything else to be type definitions
(parse-type-structures (cdr (list ,@args))))))
(defun base_case ()
(deftype 'biscuit 'int))
;; {type,{[biscuit],[int]}}
(defun list_case ()
(deftype 'foo (list 'int)))
;; {type,{[foo],["[",[int],"]"]}}
(defun tuple_case_simple ()
(deftype 'foo (tuple 'int)))
;; {type,{[foo],["{",[int],"}"]}}
(defun tuple_case_multiple ()
(deftype 'foo (tuple 'atom 'int (list 'int))))
;; {type,{[foo],["{",[atom,int,"[",[int],"]"],"}"]}}
(defun fun_spec_simple ()
(with-spec 'int '-> 'int))
;; {spec,{[int],{[int]}}}
(defun fun_spec_multiple ()
(with-spec 'int 'string '-> (list 'int)))
;; {spec,{[int,string],{["[",[int],"]"]}}}
(defun fun_spec_tuple ()
(with-spec 'int '-> (tuple 'atom (list 'pid))))
;; {spec,{[int],{["{",[atom,"[",[pid],"]"],"}"]}}}
(defun type_with_fun ()
;; This doesn't quite work because I'm trying to go too macro heavy I
;; think because I'm calling a macro from a function that was called
;; in a macro and it's not being evaluated the way I expected it to
;; be. I can see what it's doing. I'm just not macro-ninja enough to
;; correct. Though really if it was all functions then I suspect it
;; would work fine. :D
(deftype 'foobar '('fun 'int '-> 'string)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment