Skip to content
Browse files

support type specs in joxa

  • Loading branch information...
1 parent 7e16f40 commit e1ce34c218dd37c7a7b89af8edcbb89635074c14 @ericbmerritt ericbmerritt committed Jan 3, 2012
Showing with 511 additions and 30 deletions.
  1. +11 −0 features/jxat_specs.feature
  2. +14 −5 src/jxa_annot.erl
  3. +11 −1 src/jxa_compile.erl
  4. +78 −3 src/jxa_ctx.erl
  5. +40 −18 src/jxa_definition.erl
  6. +11 −3 src/jxa_expression.erl
  7. +261 −0 src/jxa_spec.erl
  8. +85 −0 test/jxat_specs.erl
View
11 features/jxat_specs.feature
@@ -0,0 +1,11 @@
+Feature: Joxa should support type declarations
+ In order to allow a developer to specify types
+ and allow functions to be predeclared.
+ As an Joxa Developer
+ I want to Joxa to support specifying types and type specs
+
+ Scenario: Support type specs
+ Given a module that declares types
+ When joxa is called on this module
+ Then a beam binary is produced
+ And the described function can be called and works correctly
View
19 src/jxa_annot.erl
@@ -49,24 +49,33 @@ get(Path, Annotations) ->
ec_dictionary:get(Path, Annotations).
get_line(Path, Annotations) ->
- {_, _, LineAnnotations} = jxa_annot:get(Path, Annotations),
+ {_, _, LineAnnotations} = get_valid_line(Path, Annotations),
LineAnnotations.
get_line(Path, Extra, Annotations) when is_list(Extra) ->
- {_, _, LineAnnotations} = jxa_annot:get(Path, Annotations),
+ {_, _, LineAnnotations} = get_valid_line(Path, Annotations),
Extra ++ LineAnnotations;
get_line(Path, Extra, Annotations) ->
- {_, _, LineAnnotations} = jxa_annot:get(Path, Annotations),
+ {_, _, LineAnnotations} = get_valid_line(Path, Annotations),
[Extra | LineAnnotations].
get_idx(Path, Annotations) ->
- {_, Idx, _} = jxa_annot:get(Path, Annotations),
+ {_, Idx, _} = get_valid_line(Path, Annotations),
Idx.
get_type(Path, Annotations) ->
- {Type, _, _} = ec_dictionary:get(Path, Annotations),
+ {Type, _, _} = get_valid_line(Path, Annotations),
Type.
%%=============================================================================
%% Internal Functions
%%=============================================================================
+get_valid_line([], _Annotations) ->
+ throw(not_found);
+get_valid_line(Path=[_ | Rest], Annotations) ->
+ try
+ ec_dictionary:get(Path, Annotations)
+ catch
+ _:not_found ->
+ get_valid_line(Rest, Annotations)
+ end.
View
12 src/jxa_compile.erl
@@ -120,7 +120,7 @@ compile_context(Ctx0) ->
jxa_ctx:module_name(Ctx1)),
Exports = [cerl:ann_c_fname(EAnnots, Fun, Arity) ||
{Fun, Arity, EAnnots} <- sets:to_list(jxa_ctx:exports(Ctx1))],
- Attrs0 = jxa_ctx:attrs(Ctx1),
+ Attrs0 = jxa_ctx:attrs(Ctx1) ++ compile_types(Ctx1),
Defs = [Value || {_, Value} <-
ec_dictionary:to_list(jxa_ctx:definitions(Ctx1))],
{Ctx1, erl_comp(cerl:ann_c_module(Annots, ModuleName,
@@ -142,6 +142,16 @@ compile_module_info(Ctx0) ->
[VarName],
ArgBody, Ctx1).
+compile_types(Ctx0) ->
+ ExportedTypes = sets:to_list(jxa_ctx:type_exports(Ctx0)),
+ AllTypes = ec_dictionary:to_list(jxa_ctx:types(Ctx0)),
+ [{cerl:make_data({atomic, export_type}, []),
+ cerl:make_data({atomic, ExportedTypes}, [])}
+ | [Attr || {_, Attr} <- AllTypes]].
+
+
+
+
-spec erl_comp(cerl:cerl()) -> binary().
erl_comp(CerlAST) ->
View
81 src/jxa_ctx.erl
@@ -33,7 +33,15 @@
add_variable_to_scope/3,
add_variables_to_scope/2,
pop_scope/1,
- update/1]).
+ update/1,
+ push_type_scope/1,
+ pop_type_scope/1,
+ add_type_variable_to_scope/3,
+ resolve_type_variable/3,
+ types/1,
+ add_type_export/3,
+ type_exports/1,
+ add_type/4]).
-export_type([context/0,
attr/0,
@@ -46,6 +54,11 @@
-record(context, {module_name :: atom(),
annots :: jxa_annot:annotations(),
exports :: set(),
+ type_exports :: set(),
+ type_scopes :: [type_scope()],
+ types :: ec_dictionary:dictionary({atom(),
+ non_neg_integer()},
+ term()),
attrs :: [attr()],
alias :: alias(),
require :: require(),
@@ -72,21 +85,26 @@
-type export() :: {Fun::atom(), Arity::non_neg_integer(),
Line::non_neg_integer()}.
-type scope() :: ec_dictionary:dictionary(atom(), cerl:var()).
+-type type_scope() :: set().
%%=============================================================================
%% Public API
%%=============================================================================
+
%% create a new context to use for compilation of the system to use during
%% compilation.
-spec new(jxa_annot:annotations()) -> context().
new(Annots) ->
#context{module_name=undefined,
attrs=[],
exports=sets:new(),
+ type_exports=sets:new(),
annots=Annots,
scopes=[],
+ type_scopes=[],
definitions=ec_dictionary:new(ec_dict),
+ types=ec_dictionary:new(ec_dict),
alias=ec_dictionary:new(ec_dict),
require=ec_dictionary:new(ec_dict),
use=ec_dictionary:new(ec_dict)
@@ -101,8 +119,11 @@ new(Annots, ModuleName, Line)
line=Line,
annots=Annots,
exports=sets:new(),
+ type_exports=sets:new(),
attrs=[],
scopes=[],
+ type_scopes=[],
+ types=ec_dictionary:new(ec_dict),
definitions=ec_dictionary:new(ec_dict),
alias=ec_dictionary:new(ec_dict),
require=ec_dictionary:new(ec_dict),
@@ -142,8 +163,11 @@ new(Annots, Aliases, Requires, Uses) ->
#context{module_name=undefined,
annots=Annots,
exports=sets:new(),
+ type_exports=sets:new(),
attrs=[],
scopes=[],
+ type_scopes=[],
+ types=ec_dictionary:new(ec_dict),
definitions=ec_dictionary:new(ec_dict),
alias=ec_dictionary:from_list(ec_dict, Aliases),
require=Req2,
@@ -319,7 +343,8 @@ check_aliases(Module, Function, PossibleArity, Ctx0 = #context{alias=Alias}) ->
end.
search_for_defined_used_function(Name, PossibleArity,
- #context{definitions=Defs, use=Uses}) ->
+ #context{types=Types,
+ definitions=Defs, use=Uses}) ->
case ec_dictionary:has_key({Name, PossibleArity}, Defs) of
true ->
{apply, Name, PossibleArity};
@@ -328,7 +353,14 @@ search_for_defined_used_function(Name, PossibleArity,
{FunName, ModuleName} ->
{remote, ModuleName, FunName};
undefined ->
- not_a_reference
+ %% Types serve as a predeclaration where needed
+ case ec_dictionary:get({Name, PossibleArity},
+ undefined, Types) of
+ undefined ->
+ not_a_reference;
+ _ ->
+ {apply, Name, PossibleArity}
+ end
end
end.
@@ -362,3 +394,46 @@ update(Ctx0=#context{module_name=ModuleName, require=Req}) ->
_:undef ->
Ctx0
end.
+
+push_type_scope(Ctx0 = #context{type_scopes=Scopes}) ->
+ Ctx0#context{type_scopes=[sets:new() | Scopes]}.
+
+pop_type_scope(Ctx0=#context{type_scopes=[_|Scopes]}) ->
+ Ctx0#context{type_scopes=Scopes}.
+
+add_type_variable_to_scope(Name, Arity,
+ Ctx0=#context{type_scopes=[Current | Scopes]}) ->
+ Ctx0#context{type_scopes=[sets:add_element({Name, Arity}, Current) | Scopes]}.
+
+resolve_type_variable(Name, Arity,
+ #context{type_scopes=TypeScopes,
+ types=Types}) ->
+ case lists:any(fun(SetScope) ->
+ sets:is_element({Name, Arity}, SetScope)
+ end, TypeScopes) of
+ true ->
+ true;
+ false ->
+ case ec_dictionary:get({Name, Arity}, undefined, Types) of
+ undefined ->
+ false;
+ _ ->
+ true
+ end
+ end.
+
+type_exports(#context{type_exports=Exports}) ->
+ Exports.
+
+-spec add_type_export(Fun::atom(), Arity::non_neg_integer(),
+ context()) ->
+ context().
+add_type_export(TypeName, Arity, Ctx0=#context{type_exports=Exports}) ->
+ Ctx0#context{type_exports=sets:add_element({TypeName, Arity}, Exports)}.
+
+types(#context{types=Types}) ->
+ Types.
+
+add_type(TypeName, Arity, Body, Ctx0=#context{types=Types}) ->
+ Ctx0#context{types=ec_dictionary:add({TypeName, Arity}, Body, Types)}.
+
View
58 src/jxa_definition.erl
@@ -7,39 +7,61 @@
%%=============================================================================
%% Public API
%%=============================================================================
-comp(Path0, Ctx0, ['defn+', Name, Args, Expression])
- when is_atom(Name), is_list(Args) ->
- Ctx1 = jxa_ctx:add_def_placeholder(Name, erlang:length(Args), Ctx0),
- {Ctx2, ArgList, Body} =
- jxa_expression:do_function_body(jxa_path:incr(2, Path0), Ctx1,
- Args, Expression),
+comp(Path0, Ctx0, ['defn+' | Details]) ->
+ {Ctx2, Name, ArgList, Body} =
+ compile_function(jxa_path:incr(Path0), Ctx0,
+ Details),
Annots = jxa_annot:get_line(jxa_path:add_path(Path0),
jxa_ctx:annots(Ctx2)),
jxa_ctx:add_exported_definition(Annots, Name, ArgList, Body, Ctx2);
-comp(Path0, Ctx0, [defn, Name, Args, Expression]) ->
- Ctx1 = jxa_ctx:add_def_placeholder(Name, erlang:length(Args), Ctx0),
- {Ctx2, ArgList, Body} =
- jxa_expression:do_function_body(jxa_path:incr(2, Path0), Ctx1,
- Args, Expression),
+comp(Path0, Ctx0, [defn | Details]) ->
+ {Ctx1, Name, ArgList, Body} =
+ compile_function(jxa_path:incr(Path0), Ctx0, Details),
Annots = jxa_annot:get_line(jxa_path:add_path(Path0),
- jxa_ctx:annots(Ctx2)),
+ jxa_ctx:annots(Ctx1)),
jxa_ctx:add_definition(Annots, Name, ArgList, Body, Ctx1);
-comp(Path0, Ctx0, [definline, Name, Args, Expression]) ->
- Ctx1 = jxa_ctx:add_def_placeholder(Name, erlang:length(Args), Ctx0),
- {Ctx2, ArgList, Body} =
- jxa_expression:do_function_body(jxa_path:incr(2, Path0), Ctx1,
- Args, Expression),
+comp(Path0, Ctx0, [definline | Details]) ->
+ {Ctx1, Name, ArgList, Body} = compile_function(jxa_path:incr(Path0),
+ Ctx0, Details),
Annots = jxa_annot:get_line(jxa_path:add_path(Path0),
inline,
- jxa_ctx:annots(Ctx2)),
+ jxa_ctx:annots(Ctx1)),
jxa_ctx:add_definition(Annots, Name, ArgList, Body, Ctx1);
+comp(Path0, Ctx0, Form = ['deftype+' | _]) ->
+ jxa_spec:comp(Path0, Ctx0, Form);
+comp(Path0, Ctx0, Form = [deftype | _]) ->
+ jxa_spec:comp(Path0, Ctx0, Form);
+comp(Path0, Ctx0, Form = [defspec | _]) ->
+ jxa_spec:comp(Path0, Ctx0, Form);
comp(Path0, Ctx0, _) ->
Idx = jxa_annot:get_idx(jxa_path:path(Path0),
jxa_ctx:annots(Ctx0)),
?JXA_THROW({invalid_definition, Idx}).
+compile_function(Path0, Ctx0, Body = [_, _, _]) ->
+ compile_function1(Path0, Ctx0, [default_type() | Body]);
+compile_function(Path0, Ctx0, Body = [_, _, _, _]) ->
+ compile_function1(jxa_path:incr(Path0), Ctx0, Body);
+compile_function(Path0, Ctx0, _) ->
+ Idx = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ ?JXA_THROW({invalid_definition, Idx}).
+compile_function1(Path0, Ctx0, [ReturnType, Name, Args, Expression])
+ when is_atom(Name), is_list(Args) ->
+ SpecArgs = lists:map(fun([SpecArgs, _Arg]) ->
+ SpecArgs;
+ (_Arg) ->
+ default_type()
+ end, Args),
+ Ctx1 = jxa_spec:comp_implicit(Path0, Ctx0, Name, SpecArgs, ReturnType),
+ {Ctx2, ArgList, Body} =
+ jxa_expression:do_function_body(jxa_path:incr(Path0), Ctx1,
+ Args, Expression),
+ {Ctx2, Name, ArgList, Body}.
+default_type() ->
+ [{'__fun__', erlang, any}].
View
14 src/jxa_expression.erl
@@ -7,9 +7,17 @@
%%=============================================================================
%% Public API
%%=============================================================================
-do_function_body(Path0, Ctx0, Args, Expression) ->
- {Ctx1, ArgList} = gen_args(jxa_path:add(Path0), Ctx0, Args),
- Ctx2 = jxa_ctx:add_variables_to_scope(Args, jxa_ctx:push_scope(Ctx1)),
+do_function_body(Path0, Ctx0, Args0, Expression) ->
+ %% Funs can have types at any level. we just ignore them at
+ %% Anything but the top
+ Args1 = lists:map(fun([_, Arg]) ->
+ Arg;
+ (Arg) ->
+ Arg
+ end, Args0),
+ {Ctx1, ArgList} = gen_args(jxa_path:add(Path0), Ctx0, Args1),
+
+ Ctx2 = jxa_ctx:add_variables_to_scope(Args1, jxa_ctx:push_scope(Ctx1)),
{Ctx3, Body} = comp(jxa_path:add(jxa_path:incr(Path0)),
Ctx2, Expression),
View
261 src/jxa_spec.erl
@@ -0,0 +1,261 @@
+%% -*- mode: Erlang; fill-column: 80; comment-column: 76; -*-
+-module(jxa_spec).
+
+-export([comp/3, comp_implicit/5]).
+-include_lib("joxa/include/joxa.hrl").
+
+%%=============================================================================
+%% Public API
+%%=============================================================================
+comp_implicit(Path0, Ctx0, Name, Args, Expression) ->
+ Annots = jxa_annot:get_line(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ Arity = erlang:length(Args),
+
+ {Ctx1, Body} = def_anon_function(jxa_path:incr(2, Path0),
+ Ctx0, Args, Expression),
+
+ jxa_ctx:add_type(Name, Arity,
+ {{c_literal,Annots,spec},
+ {c_literal,
+ Annots,
+ [{{Name,Arity},
+ [Body]}]}}, Ctx1).
+
+comp(Path0, Ctx0, ['deftype+', Name, Args, Expression])
+ when is_atom(Name), is_list(Args) ->
+ Arity = erlang:length(Args),
+ Ctx1 = jxa_ctx:add_type_export(Name, Arity, Ctx0),
+ def_top_level_function(jxa_path:incr(Path0), Ctx1,
+ Name, Args, Expression);
+comp(Path0, Ctx0, [deftype, Name, Args, Expression]) ->
+ def_top_level_function(jxa_path:incr(Path0), Ctx0,
+ Name, Args, Expression);
+comp(Path0, Ctx0, [defspec, Name, Args, Expression]) ->
+ comp_implicit(jxa_path:incr(Path0), Ctx0, Name, Args, Expression);
+comp(Path0, Ctx0, _) ->
+ Idx = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ ?JXA_THROW({invalid_type_definition, Idx}).
+
+%%========================= ====================================================
+%% Internal API
+%%=============================================================================
+def_top_level_function(Path0, Ctx0, Name, Args, Expression) ->
+ Idx = {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ Arity = erlang:length(Args),
+ Ctx1 = jxa_ctx:push_type_scope(Ctx0),
+
+ Ctx2 =
+ jxa_ctx:add_type_variable_to_scope(Name,
+ Arity,
+ Ctx1),
+ Ctx4 = lists:foldl(fun(Arg, Ctx3) when is_atom(Arg) ->
+ jxa_ctx:add_type_variable_to_scope(Arg, -1, Ctx3);
+ (_Arg, _) ->
+ ?JXA_THROW({invalid_type_definition, Idx})
+ end, Ctx2, Args),
+ {Ctx5, Expr} = comp_expr(jxa_path:incr(3, Path0),
+ Ctx4, Expression),
+
+ jxa_ctx:add_type(Name, Arity,
+ {{c_literal,[Line],type},
+ {c_literal,[Line],
+ [{Name, Expr, lists:map(fun(El) ->
+ {var, Line, El}
+ end, Args)}]}},
+ jxa_ctx:pop_type_scope(Ctx5)).
+
+def_anon_function(Path0, Ctx0, Args0, Expression) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {_, Ctx3, Args1} =
+ lists:foldl(fun(Arg, {Path1, Ctx1, Acc}) ->
+ {Ctx2, ArgExpr} = comp_expr(Path1, Ctx1, Arg),
+ {jxa_path:incr(Path1), Ctx2, [ArgExpr | Acc]}
+ end, {Path0, Ctx0, []}, Args0),
+ {Ctx5, Expr} = comp_expr(jxa_path:incr(2, Path0),
+ Ctx3, Expression),
+
+ {Ctx5, [{type,Line,'fun',
+ [{type,Line,product, lists:reverse(Args1)},
+ Expr]}]}.
+
+
+
+comp_expr(Path0, Ctx0, [quote, Value]) ->
+ {Ctx0, mk_literal(jxa_path:add(jxa_path:incr(Path0)), Ctx0, Value)};
+comp_expr(Path0, Ctx0, Arg) when is_tuple(Arg) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {_, Ctx3, TupleValues} =
+ lists:foldl(fun(El, {Path1, Ctx1, Acc}) ->
+ {Ctx2, CompEl} =
+ comp_expr(Path1,
+ Ctx1, El),
+ {jxa_path:incr(Path1), Ctx2, [CompEl | Acc]}
+ end, {Path0, Ctx0, []}, tuple_to_list(Arg)),
+ {Ctx3, {tuple, Line, list_to_tuple(lists:reverse(TupleValues))}};
+comp_expr(Path0, Ctx0, Arg) when is_integer(Arg) ->
+ {Ctx0, mk_literal(Path0, Ctx0, Arg)};
+comp_expr(Path0, Ctx0, [binary]) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {Ctx0, {type,Line,binary, [{integer,Line,0},{integer,Line,0}]}};
+comp_expr(Path0, Ctx0, [binary, Arg]) when is_integer(Arg) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {Ctx0, {type,Line,binary, [{integer,Line,Arg},{integer,Line,0}]}};
+comp_expr(Path0, Ctx0, [binary, '*', Arg]) when is_integer(Arg) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {Ctx0, {type,Line,binary, [{integer,Line,0},{integer,Line,Arg}]}};
+comp_expr(Path0, Ctx0, [binary, Arg1, '*', Arg2])
+ when is_integer(Arg1), is_integer(Arg2) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {Ctx0, {type,Line,binary, [{integer,Line,Arg1},{integer,Line,Arg2}]}};
+comp_expr(Path0, Ctx0, [binary | _]) ->
+ Idx = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ ?JXA_THROW({invalid_binary_type_spec, Idx});
+comp_expr(Path0, Ctx0, ['fn']) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {Ctx0, {type, Line, 'fun', []}};
+comp_expr(Path0, Ctx0, ['fn', ['...'], Expr]) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {Ctx1, CompExpr} = comp_expr(jxa_path:add(jxa_path:incr(2, Path0)),
+ Ctx0, Expr),
+ {Ctx1, {type, Line, 'fun', [{type, Line, any}, CompExpr]}};
+comp_expr(Path0, Ctx0, ['fn', Args, Expr]) ->
+ def_anon_function(jxa_path:incr(Path0),
+ Ctx0, Args, Expr);
+comp_expr(Path0, Ctx0, [list, Arg]) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {Ctx1, List} = comp_expr(jxa_path:add(jxa_path:incr(Path0)),
+ Ctx0, Arg),
+ {Ctx1, {list, Line, List}};
+comp_expr(Path0, Ctx0, Name) when is_atom(Name) ->
+ Idx = {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ case jxa_ctx:resolve_type_variable(Name, -1, Ctx0) of
+ true ->
+ {Ctx0, {var, Line, Name}};
+ false ->
+ ?JXA_THROW({invalid_type_reference, Idx})
+ end;
+comp_expr(Path0, Ctx0, [{'__fun__', erlang, range}, A1, A2])
+ when is_integer(A1), is_integer(A2) ->
+ %% Things in erlang get treated a bit differently then things in
+ %% other modules. It sucks that they have to be special
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {_, Ctx3, CompArgs} =
+ lists:foldl(fun(El, {Path1, Ctx1, Acc}) ->
+ {Ctx2, CompEl} =
+ comp_expr(Path1, Ctx1, El),
+ {jxa_path:incr(Path1),
+ Ctx2, [CompEl | Acc]}
+ end, {jxa_path:incr(Path0), Ctx0, []},
+ [A1, A2]),
+ {Ctx3, {type, Line, range,
+ lists:reverse(CompArgs)}};
+comp_expr(Path0, Ctx0, [{'__fun__', erlang, Func} | Args]) ->
+ %% Things in erlang get treated a bit differently then things in
+ %% other modules. It sucks that they have to be special
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {_, Ctx3, CompArgs} =
+ lists:foldl(fun(El, {Path1, Ctx1, Acc}) ->
+ {Ctx2, CompEl} =
+ comp_expr(Path1, Ctx1, El),
+ {jxa_path:incr(Path1),
+ Ctx2, [CompEl | Acc]}
+ end, {jxa_path:incr(Path0), Ctx0, []},
+ Args),
+ {Ctx3, {type, Line, Func,
+ lists:reverse(CompArgs)}};
+comp_expr(Path0, Ctx0, [{'__fun__', Module, Func} | Args])
+ when is_atom(Module), is_atom(Func) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {_, Ctx3, CompArgs} =
+ lists:foldl(fun(El, {Path1, Ctx1, Acc}) ->
+ {Ctx2, CompEl} =
+ comp_expr(Path1, Ctx1, El),
+ {jxa_path:incr(Path1),
+ Ctx2, [CompEl | Acc]}
+ end, {jxa_path:incr(Path0), Ctx0, []},
+ Args),
+ {Ctx3, {remote_type, Line, [{atom, Line, Module},
+ {atom, Line, Func},
+ lists:reverse(CompArgs)]}};
+comp_expr(Path0, Ctx0, [Var | Args]) when is_atom(Var) ->
+ ArgCount = erlang:length(Args),
+ Idx = {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ case jxa_ctx:resolve_type_variable(Var, ArgCount, Ctx0) of
+ false ->
+ ?JXA_THROW({invalid_type_reference, Idx});
+ true ->
+ {_, Ctx3, CompArgs} =
+ lists:foldl(fun(El, {Path1, Ctx1, Acc}) ->
+ {Ctx2, CompEl} =
+ comp_expr(Path1, Ctx1, El),
+ {jxa_path:incr(Path1),
+ Ctx2, [CompEl | Acc]}
+ end, {jxa_path:incr(Path0), Ctx0, []},
+ Args),
+ {Ctx3, {type, Line, Var, CompArgs}}
+ end.
+
+mk_literal(Path0, Ctx0, Arg) when is_atom(Arg) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {atom, Line, Arg};
+mk_literal(Path0, Ctx0, Arg) when is_integer(Arg) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ {integer, Line, Arg};
+mk_literal(Path0, Ctx0, [Args]) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+
+ {list, Line, mk_literal(jxa_path:add(Path0), Ctx0, Args)};
+mk_literal(Path0, Ctx0, Args) when is_tuple(Args) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ Literals = lists:foldl(fun(El, {Path1, Acc}) ->
+ {jxa_path:incr(Path1),
+ [mk_literal(Path1, Ctx0, El) |
+ Acc]}
+ end, {Path0, []}, tuple_to_list(Args)),
+ {list, Line, lists:reverse(Literals)};
+mk_literal(Path0, Ctx0, Args) when is_tuple(Args) ->
+ {Line, _} = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ Literals = lists:foldl(fun(El, {Path1, Acc}) ->
+ {jxa_path:incr(Path1),
+ [mk_literal(Path1, Ctx0, El) |
+ Acc]}
+ end, {Path0, []}, tuple_to_list(Args)),
+ {tuple, Line, lists:reverse(Literals)};
+mk_literal(Path0, Ctx0, _) ->
+ Idx = jxa_annot:get_idx(jxa_path:path(Path0),
+ jxa_ctx:annots(Ctx0)),
+ ?JXA_THROW({invalid_type_literal, Idx}).
+
+
+
+
+
+
+
+
+
+
+
View
85 test/jxat_specs.erl
@@ -0,0 +1,85 @@
+-module(jxat_specs).
+
+-export([given/3, 'when'/3, then/3]).
+-include_lib("eunit/include/eunit.hrl").
+
+given([a,module,that,declares,types], _State, _) ->
+ Source = <<"(module jxat-spec-test)
+
+ (deftype+ tunion () (erlang/union :x :y))
+ (deftype+ trange () (erlang/range 1 2))
+ (deftype+ tbinary () <<>>)
+ (deftype+ tbinary1 () <<8>>)
+ (deftype+ tbinary2 () <<* 8>>)
+ (deftype+ tbinary3 () <<7 * 8>>)
+ (deftype+ tfn () (fn (...) :ok))
+ (deftype+ tfn1(a) (fn (a 2) {:ok a}))
+ (deftype+ tfn2() (fn () :ok))
+ (deftype+ ttuple (a b) {a b 3})
+ (deftype+ ttuple1 () {})
+ (deftype+ tf () (fn))
+
+ (deftype+ foo (bar baz) {bar baz})
+ (deftype boo () :ok)
+ (deftype+ baz () (erlang/range 1 2))
+ (deftype+ hoo (a) a)
+ (defspec internal-test () (foo :this :is))
+
+ (defn internal-test ()
+ {:this :is :a :test})
+
+ (defn+ (foo :this :is) do-test1 ()
+ {:this :is :a :test})
+
+ (defn+ (boo) do-test2 (((boo) z) ((hoo :ok) y))
+ {:this :is z}) ">>,
+
+ {ok, Source}.
+
+
+'when'([joxa,is,called,on,this,module], Source, _) ->
+ Result = jxa_compile:comp("", Source),
+ {ok, Result}.
+
+then([a,beam,binary,is,produced], State = {_, Binary}, _) ->
+ ?assertMatch(true, is_binary(Binary)),
+ {ok, State};
+then([the,described,function,can,be,called,'and',works,correctly],
+ State = {_, Binary}, _) ->
+ ?assertMatch([{'do-test1',0},
+ {'do-test2',2},
+ {module_info,0},
+ {module_info,1}],
+ lists:sort('jxat-spec-test':module_info(exports))),
+ {ok, State}.
+
+%% has_spec(Beam, FA0)->
+%% {ok,{_,[{abstract_code,{_,AC}}]}} =
+%% beam_lib:chunks(Beam,[abstract_code]),
+%% lists:any(fun({attribute,_,'spec',{FA1,_}}) ->
+%% FA0 == FA1;
+%% (_) ->
+%% false
+%% end, AC).
+
+%% has_type(Beam, {F, A})->
+%% {ok,{_,[{abstract_code,{_,AC}}]}} =
+%% beam_lib:chunks(Beam,[abstract_code]),
+%% lists:any(fun({attribute,_,'type',{Fun, _, Args}}) ->
+%% F == Fun andalso
+%% erlang:length(Args) == A;
+%% (_) ->
+%% false
+%% end, AC).
+
+
+%% has_exported_type(Beam, FA0)->
+%% {ok,{_,[{abstract_code,{_,AC}}]}} =
+%% beam_lib:chunks(Beam,[abstract_code]),
+%% NTypes = lists:foldl(fun({attribute,_,'export_type', Types}, _) ->
+%% Types;
+%% (_, Acc) ->
+%% Acc
+%% end, none, AC),
+%% lists:member(FA0, NTypes).
+

0 comments on commit e1ce34c

Please sign in to comment.
Something went wrong with that request. Please try again.