Last active
March 29, 2016 21:59
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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