From 65639094c79864dee86ec34c9e15ec77fa805817 Mon Sep 17 00:00:00 2001 From: istr Date: Thu, 25 Jul 2013 12:29:59 +0200 Subject: [PATCH 1/7] Make dialyzer happy Use ets:tab() instead of ets:tid(), refine type specs such that the code passes dialyzer with options -Wunmatched_returns, -Werror_handling, -Wrace_conditions, -Wunderspecs --- src/glc.erl | 6 ++++-- src/glc_code.erl | 26 ++++++++++++++++++-------- src/glc_lib.erl | 2 ++ src/glc_ops.erl | 19 ++++++++++--------- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/glc.erl b/src/glc.erl index ecca4b7..e57647f 100644 --- a/src/glc.erl +++ b/src/glc.erl @@ -87,7 +87,7 @@ -record(module, { 'query' :: term(), - tables :: [{atom(), ets:tid()}], + tables :: [{atom(), ets:tab()}], qtree :: term() }). @@ -140,7 +140,9 @@ null(Result) -> %% Updating the output action of a query finalizes it. Attempting %% to use a finalized query to construct a new query will result %% in a `badarg' error. --spec with(glc_ops:op(), fun((gre:event()) -> term())) -> glc_ops:op(). +-type qry() :: {atom(), '*'|false|true|[any(),...]} | {atom(), '<'|'='|'>', _}. +-type action() :: fun((_) -> any()). +-spec with(Query::qry(), Action::action()) -> {with, Query::qry(), Action::action()}. with(Query, Action) -> glc_ops:with(Query, Action). diff --git a/src/glc_code.erl b/src/glc_code.erl index 810ca3c..2659aa4 100644 --- a/src/glc_code.erl +++ b/src/glc_code.erl @@ -9,9 +9,10 @@ -record(module, { 'query' :: term(), - tables :: [{atom(), ets:tid()}], + tables :: [{atom(), ets:tab()}], qtree :: term() }). +-type mod() :: #module{}. -type syntaxTree() :: erl_syntax:syntaxTree(). @@ -20,11 +21,12 @@ fields = [] :: [{atom(), syntaxTree()}], fieldc = 0 :: non_neg_integer(), paramvars = [] :: [{term(), syntaxTree()}], - paramstab = undefined :: ets:tid() + paramstab = undefined :: ets:tab() }). -type nextFun() :: fun((#state{}) -> [syntaxTree()]). +-spec compile (Module::atom(), mod()) -> {ok, Module::atom()}. compile(Module, ModuleData) -> {ok, forms, Forms} = abstract_module(Module, ModuleData), {ok, Module, Binary} = compile_forms(Forms, [nowarn_unused_vars]), @@ -74,7 +76,7 @@ abstract_module_(Module, #module{tables=Tables, qtree=Tree}=Data) -> [?erl:clause( [?erl:underscore()], none, [abstract_apply(erlang, error, [?erl:atom(badarg)])])]), - %% table(Name) -> ets:tid(). + %% table(Name) -> ets:tab(). ?erl:function( ?erl:atom(table), abstract_tables(Tables) ++ @@ -103,10 +105,14 @@ abstract_module_(Module, #module{tables=Tables, qtree=Tree}=Data) -> AbstractMod. %% @private Return the clauses of the table/1 function. +%-spec abstract_tables([{term(), atom()|binary()|maybe_improper_list()|number()|tuple()}]) -> +% ?erl:syntax_tree(). abstract_tables(Tables) -> [?erl:clause( [?erl:abstract(K)], none, [?erl:abstract(V)]) +% [?erl:tree(variable,V)]) +% [?erl:variable("V")]) || {K, V} <- Tables]. %% @private Return the clauses of the info/1 function. @@ -277,7 +283,7 @@ abstract_getparam_(Term, OnBound, #state{paramstab=Table, ] ++ OnBound(State#state{paramvars=[{Term, param_variable(Key)}|Params]}). %% @private Generate a variable name for the value of a field. --spec field_variable(atom()) -> string(). +-spec field_variable(atom()) -> nonempty_string(). field_variable(Key) -> "Field_" ++ field_variable_(atom_to_list(Key)). @@ -314,7 +320,7 @@ param_variable(Key) -> %% @private Return an expression to increment a counter. %% @todo Pass state record. Only Generate code if `statistics' is enabled. --spec abstract_count(atom()) -> syntaxTree(). +-spec abstract_count(filter|input|output) -> syntaxTree(). abstract_count(Counter) -> abstract_apply(ets, update_counter, [abstract_apply(table, [?erl:atom(counters)]), @@ -324,7 +330,7 @@ abstract_count(Counter) -> %% @private Return an expression to get the value of a counter. %% @todo Pass state record. Only Generate code if `statistics' is enabled. --spec abstract_getcount(atom()) -> [syntaxTree()]. +-spec abstract_getcount(filter|input|output) -> [syntaxTree()]. abstract_getcount(Counter) -> [abstract_apply(ets, lookup_element, [abstract_apply(table, [?erl:atom(counters)]), @@ -356,11 +362,15 @@ load_binary(Module, Binary) -> end. %% @private Apply an exported function. --spec abstract_apply(atom(), atom(), [syntaxTree()]) -> syntaxTree(). +% underspec: -spec abstract_apply(atom(), atom(), [syntaxTree()]) -> syntaxTree(). +-spec abstract_apply(erlang|ets|gre, '<'|'=:='|'>'|error|find|lookup_element|update_counter, [any(),...]) -> + syntaxTree(). abstract_apply(Module, Function, Arguments) -> ?erl:application(?erl:atom(Module), ?erl:atom(Function), Arguments). %% @private Apply a module local function. --spec abstract_apply(atom(), [syntaxTree()]) -> syntaxTree(). +% underspec: -spec abstract_apply(atom(), [syntaxTree()]) -> syntaxTree(). +-spec abstract_apply (table, [{tree, atom(), {_,_,_,_}, _}, ...]) -> + syntaxTree(). abstract_apply(Function, Arguments) -> ?erl:application(?erl:atom(Function), Arguments). diff --git a/src/glc_lib.erl b/src/glc_lib.erl index 427551f..40d1593 100644 --- a/src/glc_lib.erl +++ b/src/glc_lib.erl @@ -87,6 +87,7 @@ repeat(Query, Fun) -> %% @doc Return the output action of a query. +-spec onoutput ({_, '<'|'='|'>', _} | {_,'*'}) -> output. onoutput({_, '<', _}) -> output; onoutput({_, '=', _}) -> @@ -99,6 +100,7 @@ onoutput(Query) -> erlang:error(badarg, [Query]). %% @doc Modify the output action of a query. +-spec onoutput (term(), term()) -> no_return(). onoutput(Action, Query) -> erlang:error(badarg, [Action, Query]). diff --git a/src/glc_ops.erl b/src/glc_ops.erl index 05067c4..e7f920c 100644 --- a/src/glc_ops.erl +++ b/src/glc_ops.erl @@ -31,28 +31,28 @@ -export_type([op/0]). %% @doc Test that a field value is less than a term. --spec lt(atom(), term()) -> op(). +-spec lt(Key::atom(), Term::term()) -> {Key::atom(), '<', Term::term()}. lt(Key, Term) when is_atom(Key) -> {Key, '<', Term}; lt(Key, Term) -> erlang:error(badarg, [Key, Term]). %% @doc Test that a field value is equal to a term. --spec eq(atom(), term()) -> op(). +-spec eq(Key::atom(), Term::term()) -> {Key::atom(), '=', Term::term()}. eq(Key, Term) when is_atom(Key) -> {Key, '=', Term}; eq(Key, Term) -> erlang:error(badarg, [Key, Term]). %% @doc Test that a field value is greater than a term. --spec gt(atom(), term()) -> op(). +-spec gt(Key::atom(), Term::term()) -> {Key::atom(), '>', Term::term()}. gt(Key, Term) when is_atom(Key) -> {Key, '>', Term}; gt(Key, Term) -> erlang:error(badarg, [Key, Term]). %% @doc Test that a field value is exists. --spec wc(atom()) -> op(). +-spec wc(Key::atom()) -> {Key::atom(), '*'}. wc(Key) when is_atom(Key) -> {Key, '*'}; wc(Key) -> @@ -64,7 +64,7 @@ wc(Key) -> %% in the list must hold for the input event. The list is expected to %% be a non-empty list. If the list of filters is an empty list a `badarg' %% error will be thrown. --spec all([op()]) -> op(). +-spec all(Conds::nonempty_maybe_improper_list()) -> {all, Conds::nonempty_maybe_improper_list()}. all([_|_]=Conds) -> {all, Conds}; all(Other) -> @@ -76,7 +76,7 @@ all(Other) -> %% in the list must hold for the input event. The list is expected to be %% a non-empty list. If the list of filters is an empty list a `badarg' %% error will be thrown. --spec any([op()]) -> op(). +-spec any(Conds::nonempty_maybe_improper_list()) -> {any, Conds::nonempty_maybe_improper_list()}. any([_|_]=Conds) -> {any, Conds}; any(Other) -> @@ -84,7 +84,7 @@ any(Other) -> %% @doc Always return `true' or `false'. --spec null(boolean()) -> op(). +-spec null(Result::boolean()) -> {null, Result::boolean()} | no_return(). null(Result) when is_boolean(Result) -> {null, Result}; null(Result) -> @@ -95,7 +95,8 @@ null(Result) -> %% Updating the output action of a query finalizes it. Attempting %% to use a finalized query to construct a new query will result %% in a `badarg' error. --spec with(op(), fun((gre:event()) -> term())) -> op(). +-spec with(Query::op(), Fun::fun((gre:event()) -> term())) -> + {with, Query::op(), Fun::fun((gre:event()) -> term())} | no_return(). with(Query, Fun) when is_function(Fun, 1) -> {with, Query, Fun}; with(Query, Fun) -> @@ -111,7 +112,7 @@ with(Query, Fun) -> %% All queries are expected to be valid and have an output action other %% than the default which is `output'. If these expectations don't hold %% a `badarg' error will be thrown. --spec union([op()]) -> op(). +-spec union(Queries::[op()]) -> {union, Queries::[op()]} | no_return(). union(Queries) -> case [Query || Query <- Queries, glc_lib:onoutput(Query) =:= output] of [] -> {union, Queries}; From 420133ca2711ccb26cea6e43af7e9ff3e70772a0 Mon Sep 17 00:00:00 2001 From: istr Date: Mon, 29 Jul 2013 16:11:07 +0200 Subject: [PATCH 2/7] now passes paranoia dialyzer checks --- src/glc.erl | 2 +- src/gre.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/glc.erl b/src/glc.erl index e57647f..1ced79d 100644 --- a/src/glc.erl +++ b/src/glc.erl @@ -141,7 +141,7 @@ null(Result) -> %% to use a finalized query to construct a new query will result %% in a `badarg' error. -type qry() :: {atom(), '*'|false|true|[any(),...]} | {atom(), '<'|'='|'>', _}. --type action() :: fun((_) -> any()). +-type action() :: fun((gre:event()) -> any()). -spec with(Query::qry(), Action::action()) -> {with, Query::qry(), Action::action()}. with(Query, Action) -> glc_ops:with(Query, Action). diff --git a/src/gre.erl b/src/gre.erl index eca4e2c..bea1981 100644 --- a/src/gre.erl +++ b/src/gre.erl @@ -25,7 +25,7 @@ ]). -type event() :: {list, [{atom(), term()}]}. --export_types([event/0]). +-export_type([event/0]). %% @doc Construct an event term. -spec make(term(), [list]) -> event(). From 7b59be96ad5a84796a91452a6c72de418208512a Mon Sep 17 00:00:00 2001 From: istr Date: Mon, 29 Jul 2013 16:27:59 +0200 Subject: [PATCH 3/7] expose qry and action types --- src/glc.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/glc.erl b/src/glc.erl index 1ced79d..1150340 100644 --- a/src/glc.erl +++ b/src/glc.erl @@ -91,6 +91,11 @@ qtree :: term() }). +-export_type([qry/0, action/0]). + +-type qry() :: {atom(), '*'|false|true|[any(),...]} | {atom(), '<'|'='|'>', _}. +-type action() :: fun((gre:event()) -> any()). + -spec lt(atom(), term()) -> glc_ops:op(). lt(Key, Term) -> glc_ops:lt(Key, Term). @@ -140,8 +145,6 @@ null(Result) -> %% Updating the output action of a query finalizes it. Attempting %% to use a finalized query to construct a new query will result %% in a `badarg' error. --type qry() :: {atom(), '*'|false|true|[any(),...]} | {atom(), '<'|'='|'>', _}. --type action() :: fun((gre:event()) -> any()). -spec with(Query::qry(), Action::action()) -> {with, Query::qry(), Action::action()}. with(Query, Action) -> glc_ops:with(Query, Action). From 61c8855d2786154cff8a7cc5fbabe02412ef2b7e Mon Sep 17 00:00:00 2001 From: istr Date: Mon, 29 Jul 2013 19:40:49 +0200 Subject: [PATCH 4/7] use precise lists of op() --- src/glc_ops.erl | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/glc_ops.erl b/src/glc_ops.erl index e7f920c..1d18286 100644 --- a/src/glc_ops.erl +++ b/src/glc_ops.erl @@ -19,14 +19,21 @@ union/1 ]). +-type op_lt() :: {atom(), '<', term()}. +-type op_eq() :: {atom(), '=', term()}. +-type op_gt() :: {atom(), '>', term()}. +-type op_exists() :: {atom(), '*'}. +-type op_any() :: {any, [op(), ...]}. +-type op_all() :: {all, [op(), ...]}. +-type op_noop() :: {null, true|false}. -type op() :: - {atom(), '<', term()} | - {atom(), '=', term()} | - {atom(), '>', term()} | - {atom(), '*'} | - {any, [op(), ...]} | - {all, [op(), ...]} | - {null, true|false}. + op_lt() | + op_eq() | + op_gt() | + op_exists() | + op_any() | + op_all() | + op_noop(). -export_type([op/0]). @@ -51,7 +58,7 @@ gt(Key, Term) when is_atom(Key) -> gt(Key, Term) -> erlang:error(badarg, [Key, Term]). -%% @doc Test that a field value is exists. +%% @doc Test that a field value exists. -spec wc(Key::atom()) -> {Key::atom(), '*'}. wc(Key) when is_atom(Key) -> {Key, '*'}; @@ -64,7 +71,7 @@ wc(Key) -> %% in the list must hold for the input event. The list is expected to %% be a non-empty list. If the list of filters is an empty list a `badarg' %% error will be thrown. --spec all(Conds::nonempty_maybe_improper_list()) -> {all, Conds::nonempty_maybe_improper_list()}. +-spec all(Conds :: [op(), ...]) -> {all, Conds :: [op(), ...]}. all([_|_]=Conds) -> {all, Conds}; all(Other) -> @@ -76,7 +83,7 @@ all(Other) -> %% in the list must hold for the input event. The list is expected to be %% a non-empty list. If the list of filters is an empty list a `badarg' %% error will be thrown. --spec any(Conds::nonempty_maybe_improper_list()) -> {any, Conds::nonempty_maybe_improper_list()}. +-spec any(Conds :: [op(), ...]) -> {any, Conds :: [op(), ...]}. any([_|_]=Conds) -> {any, Conds}; any(Other) -> @@ -84,7 +91,7 @@ any(Other) -> %% @doc Always return `true' or `false'. --spec null(Result::boolean()) -> {null, Result::boolean()} | no_return(). +-spec null(Result::boolean()) -> {null, Result::boolean()}. null(Result) when is_boolean(Result) -> {null, Result}; null(Result) -> @@ -96,7 +103,7 @@ null(Result) -> %% to use a finalized query to construct a new query will result %% in a `badarg' error. -spec with(Query::op(), Fun::fun((gre:event()) -> term())) -> - {with, Query::op(), Fun::fun((gre:event()) -> term())} | no_return(). + {with, Query::op(), Fun::fun((gre:event()) -> term())}. with(Query, Fun) when is_function(Fun, 1) -> {with, Query, Fun}; with(Query, Fun) -> @@ -112,7 +119,7 @@ with(Query, Fun) -> %% All queries are expected to be valid and have an output action other %% than the default which is `output'. If these expectations don't hold %% a `badarg' error will be thrown. --spec union(Queries::[op()]) -> {union, Queries::[op()]} | no_return(). +-spec union(Queries::[op()]) -> {union, Queries::[op()]}. union(Queries) -> case [Query || Query <- Queries, glc_lib:onoutput(Query) =:= output] of [] -> {union, Queries}; From ee832718cb423dc0b2bd83fff594a8ec82625e5d Mon Sep 17 00:00:00 2001 From: istr Date: Mon, 29 Jul 2013 20:07:46 +0200 Subject: [PATCH 5/7] unify op / qry types --- src/glc.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/glc.erl b/src/glc.erl index 1150340..119cb4a 100644 --- a/src/glc.erl +++ b/src/glc.erl @@ -93,7 +93,8 @@ -export_type([qry/0, action/0]). --type qry() :: {atom(), '*'|false|true|[any(),...]} | {atom(), '<'|'='|'>', _}. +%-type qry() :: {atom(), '*'|false|true|[any(),...]} | {atom(), '<'|'='|'>', _}. +-type qry() :: glc_ops:op(). -type action() :: fun((gre:event()) -> any()). -spec lt(atom(), term()) -> glc_ops:op(). @@ -170,7 +171,7 @@ union(Queries) -> %% On success the module representing the query is returned. The module and %% data associated with the query must be released using the {@link delete/1} %% function. The name of the query module is expected to be unique. --spec compile(atom(), list()) -> {ok, atom()}. +-spec compile(Module :: atom(), Query :: qry()) -> {ok, atom()}. compile(Module, Query) -> {ok, ModuleData} = module_data(Query), case glc_code:compile(Module, ModuleData) of From 89b715cd58fd7da98f5c4c28940e17c1ccb3e130 Mon Sep 17 00:00:00 2001 From: istr Date: Mon, 29 Jul 2013 21:44:05 +0200 Subject: [PATCH 6/7] remove unused stuff that prevents doc building --- src/glc_code.erl | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/glc_code.erl b/src/glc_code.erl index 2659aa4..fcbfa87 100644 --- a/src/glc_code.erl +++ b/src/glc_code.erl @@ -305,18 +305,6 @@ field_variable_([]) -> param_variable(Key) -> ?erl:variable("Param_" ++ integer_to_list(Key)). -%% @private Generate a list of field variable names. -%% Walk the query tree and generate a safe variable name string for each field -%% that is accessed by the conditions in the query. Only allow alpha-numeric. -%%-spec field_variables(glc_ops:op()) -> [{atom(), string()}]. -%%field_variables(Query) -> -%% lists:usort(field_variables_(Query)). - -%%-spec field_variables(glc_ops:op()) -> [{atom(), string()}]. -%%field_variables_({Key, '=', _Term}) -> -%% [{Key, field_variable(Key)}]. - - %% @private Return an expression to increment a counter. %% @todo Pass state record. Only Generate code if `statistics' is enabled. From 9a7e9369193e3177a44769b436736f5f61208792 Mon Sep 17 00:00:00 2001 From: istr Date: Mon, 5 Aug 2013 10:12:02 +0200 Subject: [PATCH 7/7] fixes for edoc --- priv/edoc.css | 130 +++++++++++++++++++++++++++++++++++++++++++++++ src/glc_code.erl | 14 ++++- 2 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 priv/edoc.css diff --git a/priv/edoc.css b/priv/edoc.css new file mode 100644 index 0000000..1d50def --- /dev/null +++ b/priv/edoc.css @@ -0,0 +1,130 @@ +/* Baseline rhythm */ +body { + font-size: 16px; + font-family: Helvetica, sans-serif; + margin: 8px; +} + +p { + font-size: 1em; /* 16px */ + line-height: 1.5em; /* 24px */ + margin: 0 0 1.5em 0; +} + +h1 { + font-size: 1.5em; /* 24px */ + line-height: 1em; /* 24px */ + margin-top: 1em; + margin-bottom: 0em; +} + +h2 { + font-size: 1.375em; /* 22px */ + line-height: 1.0909em; /* 24px */ + margin-top: 1.0909em; + margin-bottom: 0em; +} + +h3 { + font-size: 1.25em; /* 20px */ + line-height: 1.2em; /* 24px */ + margin-top: 1.2em; + margin-bottom: 0em; +} + +h4 { + font-size: 1.125em; /* 18px */ + line-height: 1.3333em; /* 24px */ + margin-top: 1.3333em; + margin-bottom: 0em; +} + +.class-for-16px { + font-size: 1em; /* 16px */ + line-height: 1.5em; /* 24px */ + margin-top: 1.5em; + margin-bottom: 0em; +} + +.class-for-14px { + font-size: 0.875em; /* 14px */ + line-height: 1.7143em; /* 24px */ + margin-top: 1.7143em; + margin-bottom: 0em; +} + +ul { + margin: 0 0 1.5em 0; +} + +/* Customizations */ +body { + color: #333; +} + +tt, code, pre { + font-family: "Andale Mono", "Inconsolata", "Monaco", "DejaVu Sans Mono", monospaced; +} + +tt, code { font-size: 0.875em } + +pre { + font-size: 0.875em; /* 14px */ + line-height: 1.7143em; /* 24px */ + margin: 0 1em 1.7143em; + padding: 0 1em; + background: #eee; +} + +.navbar img, hr { display: none } + +table { + border-collapse: collapse; +} + +h1 { + border-left: 0.5em solid #fa0; + padding-left: 0.5em; +} + +h2.indextitle { + font-size: 1.25em; /* 20px */ + line-height: 1.2em; /* 24px */ + margin: -8px -8px 0.6em; + background-color: #fa0; + color: white; + padding: 0.3em; +} + +ul.index { + list-style: none; + margin-left: 0em; + padding-left: 0; +} + +ul.index li { + display: inline; + padding-right: 0.75em +} + +div.spec p { + margin-bottom: 0; + padding-left: 1.25em; + background-color: #eee; +} + +h3.function { + border-left: 0.5em solid #fa0; + padding-left: 0.5em; + background: #fc9; +} +a, a:visited, a:hover, a:active { color: #C60 } +h2 a, h3 a { color: #333 } + +i { + font-size: 0.875em; /* 14px */ + line-height: 1.7143em; /* 24px */ + margin-top: 1.7143em; + margin-bottom: 0em; + font-style: normal; +} diff --git a/src/glc_code.erl b/src/glc_code.erl index fcbfa87..97103c4 100644 --- a/src/glc_code.erl +++ b/src/glc_code.erl @@ -69,6 +69,9 @@ abstract_module_(Module, #module{tables=Tables, qtree=Tree}=Data) -> ?erl:atom(handle), ?erl:integer(1))])]), %% ]). + %% -spec info([ + %% TODO + %% ]). %% info(Name) -> Term. ?erl:function( ?erl:atom(info), @@ -76,6 +79,9 @@ abstract_module_(Module, #module{tables=Tables, qtree=Tree}=Data) -> [?erl:clause( [?erl:underscore()], none, [abstract_apply(erlang, error, [?erl:atom(badarg)])])]), + %% -spec table([ + %% TODO + %% ]). %% table(Name) -> ets:tab(). ?erl:function( ?erl:atom(table), @@ -83,6 +89,9 @@ abstract_module_(Module, #module{tables=Tables, qtree=Tree}=Data) -> [?erl:clause( [?erl:underscore()], none, [abstract_apply(erlang, error, [?erl:atom(badarg)])])]), + %% -spec handle([ + %% TODO + %% ]). %% handle(Event) - entry function ?erl:function( ?erl:atom(handle), @@ -90,7 +99,10 @@ abstract_module_(Module, #module{tables=Tables, qtree=Tree}=Data) -> [abstract_count(input), ?erl:application(none, ?erl:atom(handle_), [?erl:variable("Event")])])]), - %% input_(Node, App, Pid, Tags, Values) - filter roots + %% -spec handle_([ + %% TODO + %% ]). + %% handle_(Node, App, Pid, Tags, Values) - filter roots ?erl:function( ?erl:atom(handle_), [?erl:clause([?erl:variable("Event")], none,