alf 0.9.3 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +255 -129
- data/Gemfile +31 -1
- data/Gemfile.lock +17 -20
- data/LICENCE.md +1 -1
- data/Manifest.txt +2 -0
- data/README.md +37 -43
- data/TODO.md +1 -1
- data/alf.gemspec +10 -7
- data/alf.noespec +24 -13
- data/bin/alf +2 -2
- data/doc/commands/exec.md +16 -0
- data/doc/commands/help.md +11 -0
- data/doc/commands/main.md +33 -0
- data/doc/commands/show.md +19 -0
- data/doc/operators/non_relational/autonum.md +23 -0
- data/doc/operators/non_relational/clip.md +31 -0
- data/doc/operators/non_relational/coerce.md +15 -0
- data/doc/operators/non_relational/compact.md +20 -0
- data/doc/operators/non_relational/defaults.md +32 -0
- data/doc/operators/non_relational/generator.md +20 -0
- data/doc/operators/non_relational/sort.md +24 -0
- data/doc/operators/relational/extend.md +18 -0
- data/doc/operators/relational/group.md +27 -0
- data/doc/operators/relational/intersect.md +13 -0
- data/doc/operators/relational/join.md +27 -0
- data/doc/operators/relational/matching.md +20 -0
- data/doc/operators/relational/minus.md +12 -0
- data/doc/operators/relational/not-matching.md +20 -0
- data/doc/operators/relational/project.md +28 -0
- data/doc/operators/relational/quota.md +21 -0
- data/doc/operators/relational/rank.md +27 -0
- data/doc/operators/relational/rename.md +17 -0
- data/doc/operators/relational/restrict.md +25 -0
- data/doc/operators/relational/summarize.md +25 -0
- data/doc/operators/relational/ungroup.md +20 -0
- data/doc/operators/relational/union.md +14 -0
- data/doc/operators/relational/unwrap.md +20 -0
- data/doc/operators/relational/wrap.md +24 -0
- data/examples/csv/suppliers.csv +6 -0
- data/examples/logs/access.log +1000 -0
- data/examples/logs/combined.alf +2 -0
- data/examples/logs/hits.alf +14 -0
- data/examples/logs/not_found.alf +7 -0
- data/examples/logs/robots-cheating.alf +11 -0
- data/examples/logs/robots.alf +8 -0
- data/examples/northwind/customers.csv +92 -0
- data/examples/northwind/northwind.db +0 -0
- data/examples/northwind/orders.csv +831 -0
- data/examples/operators/clip.alf +1 -1
- data/examples/operators/database.alf +5 -6
- data/examples/operators/defaults.alf +1 -1
- data/examples/operators/group.alf +1 -1
- data/examples/operators/project.alf +2 -1
- data/examples/operators/pseudo-with.alf +2 -2
- data/examples/operators/quota.alf +2 -2
- data/examples/operators/summarize.alf +2 -2
- data/lib/alf/aggregator/aggregators.rb +77 -0
- data/lib/alf/aggregator/base.rb +95 -0
- data/lib/alf/aggregator/class_methods.rb +57 -0
- data/lib/alf/buffer/sorted.rb +48 -0
- data/lib/alf/command/class_methods.rb +27 -0
- data/lib/alf/command/doc_manager.rb +72 -0
- data/lib/alf/command/exec.rb +12 -0
- data/lib/alf/command/help.rb +31 -0
- data/lib/alf/command/main.rb +146 -0
- data/lib/alf/command/show.rb +33 -0
- data/lib/alf/environment/base.rb +37 -0
- data/lib/alf/environment/class_methods.rb +93 -0
- data/lib/alf/environment/explicit.rb +38 -0
- data/lib/alf/environment/folder.rb +62 -0
- data/lib/alf/extra/csv.rb +104 -0
- data/lib/alf/extra/logs.rb +100 -0
- data/lib/alf/extra/sequel.rb +77 -0
- data/lib/alf/{yaml.rb → extra/yaml.rb} +0 -0
- data/lib/alf/extra.rb +5 -0
- data/lib/alf/iterator/base.rb +38 -0
- data/lib/alf/iterator/class_methods.rb +22 -0
- data/lib/alf/iterator/proxy.rb +33 -0
- data/lib/alf/lispy/instance_methods.rb +157 -0
- data/lib/alf/operator/base.rb +74 -0
- data/lib/alf/operator/binary.rb +32 -0
- data/lib/alf/operator/cesure.rb +45 -0
- data/lib/alf/operator/class_methods.rb +132 -0
- data/lib/alf/operator/experimental.rb +9 -0
- data/lib/alf/operator/non_relational/autonum.rb +24 -0
- data/lib/alf/operator/non_relational/clip.rb +20 -0
- data/lib/alf/operator/non_relational/coerce.rb +21 -0
- data/lib/alf/operator/non_relational/compact.rb +62 -0
- data/lib/alf/operator/non_relational/defaults.rb +25 -0
- data/lib/alf/operator/non_relational/generator.rb +38 -0
- data/lib/alf/operator/non_relational/sort.rb +23 -0
- data/lib/alf/operator/nullary.rb +20 -0
- data/lib/alf/operator/relational/extend.rb +24 -0
- data/lib/alf/operator/relational/group.rb +32 -0
- data/lib/alf/operator/relational/intersect.rb +37 -0
- data/lib/alf/operator/relational/join.rb +106 -0
- data/lib/alf/operator/relational/matching.rb +45 -0
- data/lib/alf/operator/relational/minus.rb +37 -0
- data/lib/alf/operator/relational/not_matching.rb +45 -0
- data/lib/alf/operator/relational/project.rb +22 -0
- data/lib/alf/operator/relational/quota.rb +51 -0
- data/lib/alf/operator/relational/rank.rb +55 -0
- data/lib/alf/operator/relational/rename.rb +19 -0
- data/lib/alf/operator/relational/restrict.rb +20 -0
- data/lib/alf/operator/relational/summarize.rb +83 -0
- data/lib/alf/operator/relational/ungroup.rb +25 -0
- data/lib/alf/operator/relational/union.rb +32 -0
- data/lib/alf/operator/relational/unwrap.rb +21 -0
- data/lib/alf/operator/relational/wrap.rb +22 -0
- data/lib/alf/operator/shortcut.rb +53 -0
- data/lib/alf/operator/signature.rb +262 -0
- data/lib/alf/operator/transform.rb +27 -0
- data/lib/alf/operator/unary.rb +38 -0
- data/lib/alf/reader/alf_file.rb +24 -0
- data/lib/alf/reader/base.rb +119 -0
- data/lib/alf/reader/class_methods.rb +82 -0
- data/lib/alf/reader/rash.rb +28 -0
- data/lib/alf/relation/class_methods.rb +37 -0
- data/lib/alf/relation/instance_methods.rb +127 -0
- data/lib/alf/renderer/base.rb +72 -0
- data/lib/alf/renderer/class_methods.rb +58 -0
- data/lib/alf/renderer/rash.rb +19 -0
- data/lib/alf/{text.rb → renderer/text.rb} +1 -1
- data/lib/alf/tools/coerce.rb +14 -0
- data/lib/alf/tools/miscellaneous.rb +77 -0
- data/lib/alf/tools/to_lispy.rb +99 -0
- data/lib/alf/tools/to_ruby_literal.rb +14 -0
- data/lib/alf/tools/tuple_handle.rb +50 -0
- data/lib/alf/types/attr_list.rb +56 -0
- data/lib/alf/types/attr_name.rb +28 -0
- data/lib/alf/types/boolean.rb +12 -0
- data/lib/alf/types/heading.rb +96 -0
- data/lib/alf/types/ordering.rb +93 -0
- data/lib/alf/types/renaming.rb +57 -0
- data/lib/alf/types/summarization.rb +76 -0
- data/lib/alf/types/tuple_computation.rb +61 -0
- data/lib/alf/types/tuple_expression.rb +61 -0
- data/lib/alf/types/tuple_predicate.rb +49 -0
- data/lib/alf/version.rb +2 -2
- data/lib/alf.rb +193 -3714
- data/spec/integration/__database__/group.alf +1 -1
- data/spec/integration/__database__/suppliers_csv.csv +6 -0
- data/spec/integration/command/alf/alf.db +0 -0
- data/spec/integration/command/alf/alf_env_sqlite.cmd +1 -0
- data/spec/integration/command/alf/alf_env_sqlite.stdout +9 -0
- data/spec/integration/command/alf/alf_help.cmd +1 -0
- data/spec/integration/command/alf/alf_help.stdout +67 -0
- data/spec/integration/command/autonum/autonum_0.cmd +1 -1
- data/spec/integration/command/coerce/coerce_1.cmd +1 -0
- data/spec/integration/command/coerce/coerce_1.stdout +5 -0
- data/spec/integration/command/defaults/defaults_0.cmd +1 -1
- data/spec/integration/command/defaults/defaults_0.stdout +9 -9
- data/spec/integration/command/defaults/defaults_2.cmd +1 -0
- data/spec/integration/command/defaults/defaults_2.stdout +9 -0
- data/spec/integration/command/generator/generator_1.cmd +1 -0
- data/spec/integration/command/generator/generator_1.stdout +10 -0
- data/spec/integration/command/generator/generator_2.cmd +1 -0
- data/spec/integration/command/generator/generator_2.stdout +5 -0
- data/spec/integration/command/generator/generator_3.cmd +1 -0
- data/spec/integration/command/generator/generator_3.stdout +5 -0
- data/spec/integration/command/group/group_0.cmd +1 -1
- data/spec/integration/command/group/group_1.cmd +1 -1
- data/spec/integration/command/help/help_1.cmd +1 -0
- data/spec/integration/command/help/help_1.stdout +22 -0
- data/spec/integration/command/quota/quota_0.cmd +1 -1
- data/spec/integration/command/rank/rank_1.cmd +1 -1
- data/spec/integration/command/rank/rank_1.stdout +10 -10
- data/spec/integration/command/rank/rank_2.cmd +1 -1
- data/spec/integration/command/rank/rank_2.stdout +10 -10
- data/spec/integration/command/rank/rank_3.cmd +1 -1
- data/spec/integration/command/rank/rank_3.stdout +10 -10
- data/spec/integration/command/rank/rank_4.cmd +1 -1
- data/spec/integration/command/rank/rank_5.cmd +1 -1
- data/spec/integration/command/show/show_csv.cmd +1 -0
- data/spec/integration/command/show/show_csv.stdout +6 -0
- data/spec/integration/command/show/show_rash_2.cmd +1 -1
- data/spec/integration/command/show/show_rash_2.stdout +5 -5
- data/spec/integration/command/sort/sort_0.cmd +1 -1
- data/spec/integration/command/sort/sort_1.cmd +1 -1
- data/spec/integration/command/sort/sort_1.stdout +2 -2
- data/spec/integration/command/sort/sort_2.cmd +1 -0
- data/spec/integration/command/sort/sort_2.stdout +9 -0
- data/spec/integration/command/sort/sort_3.cmd +1 -0
- data/spec/integration/command/sort/sort_3.stdout +9 -0
- data/spec/integration/command/summarize/summarize_0.cmd +1 -1
- data/spec/integration/command/ungroup/ungroup_0.cmd +1 -1
- data/spec/integration/command/wrap/wrap_0.cmd +1 -1
- data/spec/integration/semantics/test_project.alf +5 -6
- data/spec/integration/semantics/test_rank.alf +16 -16
- data/spec/integration/test_command.rb +17 -6
- data/spec/integration/test_examples.rb +1 -1
- data/spec/regression/logs/apache_combined.log +5 -0
- data/spec/regression/logs/test_path_attribute.rb +25 -0
- data/spec/regression/relation/test_relation_allbut_all.rb +14 -0
- data/spec/shared/an_operator_class.rb +10 -5
- data/spec/spec_helper.rb +1 -7
- data/spec/unit/assumptions/test_set.rb +64 -0
- data/spec/unit/command/doc_manager/dynamic.md +1 -0
- data/spec/unit/command/doc_manager/example.md +1 -0
- data/spec/unit/command/doc_manager/example_1.txt +11 -0
- data/spec/unit/command/doc_manager/static.md +1 -0
- data/spec/unit/command/doc_manager/test_call.rb +49 -0
- data/spec/unit/csv/input.csv +3 -0
- data/spec/unit/csv/test_reader.rb +66 -0
- data/spec/unit/csv/test_renderer.rb +73 -0
- data/spec/unit/lispy/test_relation.rb +37 -0
- data/spec/unit/lispy/test_run.rb +40 -0
- data/spec/unit/lispy/test_tuple.rb +36 -0
- data/spec/unit/logs/apache_combined.log +5 -0
- data/spec/unit/logs/postgresql.log +29 -0
- data/spec/unit/logs/test_reader.rb +56 -0
- data/spec/unit/operator/non_relational/compact/{buffer_based.rb → test_buffer_based.rb} +0 -0
- data/spec/unit/operator/non_relational/test_clip.rb +1 -1
- data/spec/unit/operator/non_relational/test_coerce.rb +35 -0
- data/spec/unit/operator/non_relational/test_defaults.rb +15 -2
- data/spec/unit/operator/non_relational/test_generator.rb +78 -0
- data/spec/unit/operator/relational/join/test_hash_based.rb +4 -4
- data/spec/unit/operator/relational/matching/test_hash_based.rb +6 -6
- data/spec/unit/operator/relational/not_matching/test_hash_based.rb +4 -4
- data/spec/unit/operator/relational/summarize/test_hash_based.rb +10 -6
- data/spec/unit/operator/relational/summarize/test_sort_based.rb +18 -7
- data/spec/unit/operator/relational/test_group.rb +8 -8
- data/spec/unit/operator/relational/test_intersect.rb +3 -3
- data/spec/unit/operator/relational/test_minus.rb +3 -3
- data/spec/unit/operator/relational/test_project.rb +12 -2
- data/spec/unit/operator/relational/test_quota.rb +5 -6
- data/spec/unit/operator/relational/test_summarize.rb +9 -11
- data/spec/unit/operator/relational/test_union.rb +1 -1
- data/spec/unit/operator/relational/test_wrap.rb +1 -1
- data/spec/unit/operator/signature/test_collect_on.rb +45 -0
- data/spec/unit/operator/signature/test_initialize.rb +17 -0
- data/spec/unit/operator/signature/test_install.rb +56 -0
- data/spec/unit/operator/signature/test_option_parser.rb +36 -0
- data/spec/unit/operator/signature/test_parse_args.rb +60 -0
- data/spec/unit/operator/signature/test_parse_argv.rb +87 -0
- data/spec/unit/operator/signature/test_to_lispy.rb +102 -0
- data/spec/unit/operator/signature/test_to_shell.rb +103 -0
- data/spec/unit/operator/test_non_relational.rb +3 -1
- data/spec/unit/relation/test_relops.rb +20 -15
- data/spec/unit/sequel/alf.db +0 -0
- data/spec/unit/sequel/test_environment.rb +54 -0
- data/spec/unit/test_aggregator.rb +32 -22
- data/spec/unit/test_environment.rb +5 -0
- data/spec/unit/test_lispy.rb +4 -0
- data/spec/unit/test_relation.rb +5 -0
- data/spec/unit/text/test_cell.rb +6 -6
- data/spec/unit/text/test_row.rb +3 -3
- data/spec/unit/text/test_table.rb +6 -6
- data/spec/unit/tools/test_coalesce.rb +15 -0
- data/spec/unit/tools/test_coerce.rb +10 -0
- data/spec/unit/tools/test_to_lispy.rb +138 -0
- data/spec/unit/tools/test_to_ruby_literal.rb +10 -0
- data/spec/unit/tools/test_tuple_handle.rb +1 -59
- data/spec/unit/types/test_attr_list.rb +106 -0
- data/spec/unit/types/test_attr_name.rb +52 -0
- data/spec/unit/{test_heading.rb → types/test_heading.rb} +10 -0
- data/spec/unit/types/test_ordering.rb +127 -0
- data/spec/unit/types/test_renaming.rb +55 -0
- data/spec/unit/types/test_summarization.rb +63 -0
- data/spec/unit/types/test_tuple_computation.rb +60 -0
- data/spec/unit/types/test_tuple_expression.rb +64 -0
- data/spec/unit/types/test_tuple_predicate.rb +79 -0
- data/tasks/debug_mail.rake +1 -1
- data/tasks/debug_mail.txt +5 -0
- data/tasks/gh-pages.rake +63 -0
- metadata +325 -52
- data/spec/unit/operator/test_command_methods.rb +0 -38
- data/spec/unit/tools/test_ordering_key.rb +0 -94
- data/spec/unit/tools/test_parse_commandline_args.rb +0 -47
- data/spec/unit/tools/test_projection_key.rb +0 -83
data/examples/operators/clip.alf
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
:suppliers => (
|
|
3
|
-
:parts => (
|
|
4
|
-
:
|
|
5
|
-
|
|
6
|
-
}]
|
|
1
|
+
Relation({
|
|
2
|
+
:suppliers => (Relation :suppliers),
|
|
3
|
+
:parts => (Relation :parts),
|
|
4
|
+
:supplies => (Relation :supplies),
|
|
5
|
+
})
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# What is the sum of supplied quantities by supplier?
|
|
4
4
|
(summarize :supplies,
|
|
5
5
|
[:sid],
|
|
6
|
-
:total_qty =>
|
|
6
|
+
:total_qty => sum{ qty })
|
|
7
7
|
|
|
8
8
|
# Give the maximal supplied quantity by country, taking only into account
|
|
9
9
|
# suppliers that have a status greater than 10
|
|
@@ -13,4 +13,4 @@
|
|
|
13
13
|
:supplies),
|
|
14
14
|
:cities),
|
|
15
15
|
[:country],
|
|
16
|
-
:maxqty =>
|
|
16
|
+
:maxqty => sum{ qty })
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
class Aggregator
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# Defines a COUNT aggregation operator
|
|
6
|
+
#
|
|
7
|
+
class Count < Aggregator
|
|
8
|
+
def least(); 0; end
|
|
9
|
+
def happens(memo, tuple) memo + 1; end
|
|
10
|
+
end # class Count
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# Defines a SUM aggregation operator
|
|
14
|
+
#
|
|
15
|
+
class Sum < Aggregator
|
|
16
|
+
def least(); 0; end
|
|
17
|
+
def _happens(memo, val) memo + val; end
|
|
18
|
+
end # class Sum
|
|
19
|
+
|
|
20
|
+
#
|
|
21
|
+
# Defines an AVG aggregation operator
|
|
22
|
+
#
|
|
23
|
+
class Avg < Aggregator
|
|
24
|
+
def least(); [0.0, 0.0]; end
|
|
25
|
+
def _happens(memo, val) [memo.first + val, memo.last + 1]; end
|
|
26
|
+
def finalize(memo) memo.first / memo.last end
|
|
27
|
+
end # class Sum
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# Defines a MIN aggregation operator
|
|
31
|
+
#
|
|
32
|
+
class Min < Aggregator
|
|
33
|
+
def least(); nil; end
|
|
34
|
+
def _happens(memo, val)
|
|
35
|
+
memo.nil? ? val : (memo < val ? memo : val)
|
|
36
|
+
end
|
|
37
|
+
end # class Min
|
|
38
|
+
|
|
39
|
+
#
|
|
40
|
+
# Defines a MAX aggregation operator
|
|
41
|
+
#
|
|
42
|
+
class Max < Aggregator
|
|
43
|
+
def least(); nil; end
|
|
44
|
+
def _happens(memo, val)
|
|
45
|
+
memo.nil? ? val : (memo > val ? memo : val)
|
|
46
|
+
end
|
|
47
|
+
end # class Max
|
|
48
|
+
|
|
49
|
+
#
|
|
50
|
+
# Defines a COLLECT aggregation operator
|
|
51
|
+
#
|
|
52
|
+
class Collect < Aggregator
|
|
53
|
+
def least(); []; end
|
|
54
|
+
def _happens(memo, val)
|
|
55
|
+
memo << val
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#
|
|
60
|
+
# Defines a CONCAT aggregation operator
|
|
61
|
+
#
|
|
62
|
+
class Concat < Aggregator
|
|
63
|
+
def least(); ""; end
|
|
64
|
+
def default_options
|
|
65
|
+
{:before => "", :after => "", :between => ""}
|
|
66
|
+
end
|
|
67
|
+
def _happens(memo, val)
|
|
68
|
+
memo << options[:between].to_s unless memo.empty?
|
|
69
|
+
memo << val.to_s
|
|
70
|
+
end
|
|
71
|
+
def finalize(memo)
|
|
72
|
+
options[:before].to_s + memo + options[:after].to_s
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end # class Aggregator
|
|
77
|
+
end # module Alf
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
class Aggregator
|
|
3
|
+
module Base
|
|
4
|
+
|
|
5
|
+
# @return [Hash] Aggregation options
|
|
6
|
+
attr_reader :options
|
|
7
|
+
|
|
8
|
+
# @return [TupleExpression] the underlying functor
|
|
9
|
+
attr_reader :functor
|
|
10
|
+
|
|
11
|
+
# @return [String] source code of the aggregator, if any
|
|
12
|
+
attr_accessor :source
|
|
13
|
+
|
|
14
|
+
#
|
|
15
|
+
# Creates an Aggregator instance.
|
|
16
|
+
#
|
|
17
|
+
# Example:
|
|
18
|
+
#
|
|
19
|
+
# Aggregator.new{ size * price }
|
|
20
|
+
#
|
|
21
|
+
def initialize(options = {}, &block)
|
|
22
|
+
@handle = Tools::TupleHandle.new
|
|
23
|
+
@options = default_options.merge(options)
|
|
24
|
+
@functor = Tools.coerce(block, TupleExpression)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
# Returns the default options to use
|
|
29
|
+
#
|
|
30
|
+
def default_options
|
|
31
|
+
{}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
# Returns the least value, which is the one to use on an empty
|
|
36
|
+
# set.
|
|
37
|
+
#
|
|
38
|
+
# This method is intended to be overriden by subclasses; default
|
|
39
|
+
# implementation returns nil.
|
|
40
|
+
#
|
|
41
|
+
def least
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
#
|
|
46
|
+
# This method is called on each aggregated tuple and must return
|
|
47
|
+
# an updated _memo_ value. It can be seen as the block typically
|
|
48
|
+
# given to Enumerable.inject.
|
|
49
|
+
#
|
|
50
|
+
# The default implementation collects the pre-value on the tuple
|
|
51
|
+
# and delegates to _happens.
|
|
52
|
+
#
|
|
53
|
+
def happens(memo, tuple)
|
|
54
|
+
_happens(memo, @functor.evaluate(@handle.set(tuple)))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
#
|
|
58
|
+
# This method finalizes a computation.
|
|
59
|
+
#
|
|
60
|
+
# Argument _memo_ is either _least_ or the result of aggregating
|
|
61
|
+
# through _happens_. The default implementation simply returns
|
|
62
|
+
# _memo_. The method is intended to be overriden for complex
|
|
63
|
+
# aggregations that need statefull information. See Avg for an
|
|
64
|
+
# example
|
|
65
|
+
#
|
|
66
|
+
def finalize(memo)
|
|
67
|
+
memo
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
#
|
|
71
|
+
# Aggregates over an enumeration of tuples.
|
|
72
|
+
#
|
|
73
|
+
def aggregate(enum)
|
|
74
|
+
finalize(
|
|
75
|
+
enum.inject(least){|memo,tuple|
|
|
76
|
+
happens(memo, tuple)
|
|
77
|
+
})
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
protected
|
|
81
|
+
|
|
82
|
+
#
|
|
83
|
+
# @see happens.
|
|
84
|
+
#
|
|
85
|
+
# This method is intended to be overriden and returns _value_
|
|
86
|
+
# by default, making this aggregator a "Last" one...
|
|
87
|
+
#
|
|
88
|
+
def _happens(memo, value)
|
|
89
|
+
value
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
end # module Base
|
|
93
|
+
include(Base)
|
|
94
|
+
end # class Aggregator
|
|
95
|
+
end # module Alf
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
class Aggregator
|
|
3
|
+
module ClassMethods
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# Returns registered aggregators as an Array of classes
|
|
7
|
+
#
|
|
8
|
+
def aggregators
|
|
9
|
+
@aggregators ||= []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# Yields aggregator classes in turn
|
|
14
|
+
#
|
|
15
|
+
def each
|
|
16
|
+
aggregators.each(&Proc.new)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
#
|
|
20
|
+
# Automatically installs factory methods for inherited classes.
|
|
21
|
+
#
|
|
22
|
+
# Example:
|
|
23
|
+
# class Sum < Aggregate # will give a method Aggregator.sum
|
|
24
|
+
# ...
|
|
25
|
+
# end
|
|
26
|
+
# Aggregator.sum{ size }
|
|
27
|
+
#
|
|
28
|
+
def inherited(clazz)
|
|
29
|
+
aggregators << clazz
|
|
30
|
+
basename = Tools.ruby_case(Tools.class_name(clazz))
|
|
31
|
+
instance_eval <<-EOF
|
|
32
|
+
def #{basename}(*args, &block)
|
|
33
|
+
#{clazz}.new(*args, &block)
|
|
34
|
+
end
|
|
35
|
+
EOF
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#
|
|
39
|
+
# Coerces `arg` as an Aggregator
|
|
40
|
+
#
|
|
41
|
+
def coerce(arg)
|
|
42
|
+
case arg
|
|
43
|
+
when Aggregator
|
|
44
|
+
arg
|
|
45
|
+
when String
|
|
46
|
+
agg = instance_eval(arg)
|
|
47
|
+
agg.source = arg
|
|
48
|
+
agg
|
|
49
|
+
else
|
|
50
|
+
raise ArgumentError, "Invalid arg `arg` for Aggregator()"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end # module ClassMethods
|
|
55
|
+
extend(ClassMethods)
|
|
56
|
+
end # class Aggregator
|
|
57
|
+
end # module Alf
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
class Buffer
|
|
3
|
+
#
|
|
4
|
+
# Keeps tuples ordered on a specific key
|
|
5
|
+
#
|
|
6
|
+
# Example:
|
|
7
|
+
#
|
|
8
|
+
# sorted = Buffer::Sorted.new Ordering.new(...)
|
|
9
|
+
# sorted.add_all(...)
|
|
10
|
+
# sorted.each do |tuple|
|
|
11
|
+
# # tuples are ordered here
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
class Sorted < Buffer
|
|
15
|
+
|
|
16
|
+
#
|
|
17
|
+
# Creates a buffer instance with an ordering key
|
|
18
|
+
#
|
|
19
|
+
def initialize(ordering)
|
|
20
|
+
@ordering = ordering
|
|
21
|
+
@buffer = []
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
#
|
|
25
|
+
# Adds all elements of an iterator to the buffer
|
|
26
|
+
#
|
|
27
|
+
def add_all(enum)
|
|
28
|
+
sorter = @ordering.sorter
|
|
29
|
+
@buffer = merge_sort(@buffer, enum.to_a.sort(&sorter), sorter)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# (see Buffer#each)
|
|
34
|
+
#
|
|
35
|
+
def each
|
|
36
|
+
@buffer.each(&Proc.new)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# Implements a merge sort between two iterators s1 and s2
|
|
42
|
+
def merge_sort(s1, s2, sorter)
|
|
43
|
+
(s1 + s2).sort(&sorter)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end # class Sorted
|
|
47
|
+
end # class Buffer
|
|
48
|
+
end # module Alf
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Command
|
|
3
|
+
module ClassMethods
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# Returns true
|
|
7
|
+
#
|
|
8
|
+
def command?
|
|
9
|
+
true
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# Returns false
|
|
14
|
+
#
|
|
15
|
+
def operator?
|
|
16
|
+
false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.included(mod)
|
|
22
|
+
mod.extend(ClassMethods)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end # module Command
|
|
26
|
+
end # module Alf
|
|
27
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Command
|
|
3
|
+
class DocManager
|
|
4
|
+
|
|
5
|
+
# Main documentation folder
|
|
6
|
+
DOC_FOLDER = File.expand_path('../../../../doc/', __FILE__)
|
|
7
|
+
|
|
8
|
+
#
|
|
9
|
+
# Called by Quickl when it's time to generate documentation of `cmd`.
|
|
10
|
+
#
|
|
11
|
+
def call(cmd, options = {})
|
|
12
|
+
if File.exists?(file = find_file(cmd))
|
|
13
|
+
text = File.read(file)
|
|
14
|
+
|
|
15
|
+
# Replace occurences of #{signature} to #{signature.to_xxx}
|
|
16
|
+
# according to options
|
|
17
|
+
method = (options[:method] || "shell").to_s
|
|
18
|
+
text = text.gsub('#(signature)', '#(signature.to_' + method + ')')
|
|
19
|
+
|
|
20
|
+
# Replace occurences of #{...} on single lines
|
|
21
|
+
text = text.gsub(/^([ \t]*)#\(([^\)]+)\)/){|match|
|
|
22
|
+
spacing, invocation = $1, $2
|
|
23
|
+
res = cmd.instance_eval(invocation).to_s
|
|
24
|
+
realign(res, spacing, true)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Replace occurences of #{...} in other places
|
|
28
|
+
text = text.gsub(/#\(([^\)]+)\)/){|match|
|
|
29
|
+
cmd.instance_eval($1).to_s
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Replace occurences of !{...} by the execution of the example
|
|
33
|
+
text = text.gsub(/^([ \t]*)!\(([^\)]+)\)/){|match|
|
|
34
|
+
spacing, invocation = $1, $2
|
|
35
|
+
args = Quickl.parse_commandline_args(invocation)[1..-1]
|
|
36
|
+
op = Alf.lispy(Alf::Environment.examples).run(args)
|
|
37
|
+
res = op.to_rel.to_s
|
|
38
|
+
res = realign(res, spacing, false)[0...-1]
|
|
39
|
+
if options[:method] == :lispy
|
|
40
|
+
invocation = Tools.to_lispy(op)
|
|
41
|
+
end
|
|
42
|
+
realign("$ #{invocation}\n\n#{res}", spacing, false)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
else
|
|
46
|
+
"Sorry, no documentation available for #{cmd.command_name}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def realign(txt, spacing, strip)
|
|
53
|
+
rx = strip ? /^[ \t]*/ : /^/
|
|
54
|
+
txt.gsub(rx, spacing)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def find_file(cmd)
|
|
58
|
+
where = if cmd.command?
|
|
59
|
+
"commands"
|
|
60
|
+
elsif cmd.operator? && cmd.relational?
|
|
61
|
+
File.join("operators", "relational")
|
|
62
|
+
elsif cmd.operator? && cmd.non_relational?
|
|
63
|
+
File.join("operators", "non_relational")
|
|
64
|
+
else
|
|
65
|
+
raise "Unexpected command #{cmd}"
|
|
66
|
+
end
|
|
67
|
+
File.join(DOC_FOLDER, where, "#{cmd.command_name}.md")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
end # class DocManager
|
|
71
|
+
end # module Command
|
|
72
|
+
end # module Alf
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Command
|
|
3
|
+
class Help < Alf::Command()
|
|
4
|
+
include Command
|
|
5
|
+
|
|
6
|
+
# Let NoSuchCommandError be passed to higher stage
|
|
7
|
+
no_react_to Quickl::NoSuchCommand
|
|
8
|
+
|
|
9
|
+
options do |opt|
|
|
10
|
+
@dialect = :shell
|
|
11
|
+
opt.on('--lispy',
|
|
12
|
+
'Display operator signatures in lispy DSL') do
|
|
13
|
+
@dialect = :lispy
|
|
14
|
+
end
|
|
15
|
+
opt.on('--shell',
|
|
16
|
+
'Display operator signatures in shell DSL') do
|
|
17
|
+
@dialect = :shell
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Command execution
|
|
22
|
+
def execute(args)
|
|
23
|
+
sup = Quickl.super_command(self)
|
|
24
|
+
sub = (args.size != 1) ? sup : Quickl.sub_command!(sup, args.first)
|
|
25
|
+
doc = sub.documentation(:method => @dialect)
|
|
26
|
+
puts doc
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end # class Help
|
|
30
|
+
end # module Command
|
|
31
|
+
end # module Alf
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Command
|
|
3
|
+
class Main < Alf::Delegator()
|
|
4
|
+
include Command
|
|
5
|
+
|
|
6
|
+
module ClassMethods
|
|
7
|
+
|
|
8
|
+
def relational_operators(sort_by_name = false)
|
|
9
|
+
ops = subcommands.select{|cmd|
|
|
10
|
+
cmd.include?(Alf::Operator::Relational) &&
|
|
11
|
+
!cmd.include?(Alf::Operator::Experimental)
|
|
12
|
+
}
|
|
13
|
+
sort_operators(ops, sort_by_name)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def experimental_operators(sort_by_name = false)
|
|
17
|
+
ops = subcommands.select{|cmd|
|
|
18
|
+
cmd.include?(Alf::Operator::Relational) &&
|
|
19
|
+
cmd.include?(Alf::Operator::Experimental)
|
|
20
|
+
}
|
|
21
|
+
sort_operators(ops, sort_by_name)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def non_relational_operators(sort_by_name = false)
|
|
25
|
+
ops = subcommands.select{|cmd|
|
|
26
|
+
cmd.include?(Alf::Operator::NonRelational)
|
|
27
|
+
}
|
|
28
|
+
sort_operators(ops, sort_by_name)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def other_non_relational_commands(sort_by_name = false)
|
|
32
|
+
ops = subcommands.select{|cmd|
|
|
33
|
+
cmd.include?(Alf::Command)
|
|
34
|
+
}
|
|
35
|
+
sort_operators(ops, sort_by_name)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def sort_operators(ops, sort_by_name)
|
|
41
|
+
sort_by_name ? ops.sort{|op1,op2|
|
|
42
|
+
op1.command_name.to_s <=> op2.command_name.to_s
|
|
43
|
+
} : ops
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
extend(ClassMethods)
|
|
48
|
+
|
|
49
|
+
# Environment instance to use to get base iterators
|
|
50
|
+
attr_accessor :environment
|
|
51
|
+
|
|
52
|
+
# The reader to use when stdin is used as operand
|
|
53
|
+
attr_accessor :stdin_reader
|
|
54
|
+
|
|
55
|
+
# Output renderer
|
|
56
|
+
attr_accessor :renderer
|
|
57
|
+
|
|
58
|
+
# Creates a command instance
|
|
59
|
+
def initialize(env = Environment.default)
|
|
60
|
+
@environment = env
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Install options
|
|
64
|
+
options do |opt|
|
|
65
|
+
@execute = false
|
|
66
|
+
opt.on("-e", "--execute", "Execute one line of script (Lispy API)") do
|
|
67
|
+
@execute = true
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
@renderer = nil
|
|
71
|
+
Renderer.each_renderer do |name,descr,clazz|
|
|
72
|
+
opt.on("--#{name}", "Render output #{descr}"){
|
|
73
|
+
@renderer = clazz.new
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
opt.on('--env=ENV',
|
|
78
|
+
"Set the environment to use") do |value|
|
|
79
|
+
@environment = Environment.autodetect(value)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
@input_reader = :rash
|
|
83
|
+
readers = Reader.readers.collect{|r| r.first}
|
|
84
|
+
opt.on('--input-reader=READER', readers,
|
|
85
|
+
"Specify the kind of reader when reading on $stdin "\
|
|
86
|
+
"(#{readers.join(',')})") do |value|
|
|
87
|
+
@input_reader = value.to_sym
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
opt.on("-Idirectory",
|
|
91
|
+
"Specify $LOAD_PATH directory (may be used more than once)") do |val|
|
|
92
|
+
$LOAD_PATH.unshift val
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
opt.on('-rlibrary',
|
|
96
|
+
"Require the library, before executing alf") do |value|
|
|
97
|
+
require(value)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
opt.on_tail('-h', "--help", "Show help") do
|
|
101
|
+
raise Quickl::Help
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
opt.on_tail('-v', "--version", "Show version") do
|
|
105
|
+
raise Quickl::Exit, "alf #{Alf::VERSION}"\
|
|
106
|
+
" (c) 2011, Bernard Lambeau"
|
|
107
|
+
end
|
|
108
|
+
end # Alf's options
|
|
109
|
+
|
|
110
|
+
#
|
|
111
|
+
# Returns the $stdin Reader to use, according to the
|
|
112
|
+
# --input-reader= option
|
|
113
|
+
#
|
|
114
|
+
def stdin_reader
|
|
115
|
+
@stdin_reader || Reader.send(@input_reader, $stdin)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
#
|
|
119
|
+
# Executes the command on an array of arguments.
|
|
120
|
+
#
|
|
121
|
+
def execute(argv)
|
|
122
|
+
# 1) special case where a .alf file is provided
|
|
123
|
+
if argv.empty? or (argv.size == 1 && File.exists?(argv.first))
|
|
124
|
+
argv.unshift("exec")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# 2) build the operator according to -e option
|
|
128
|
+
operator = if @execute
|
|
129
|
+
Alf.lispy(environment).compile(argv.first)
|
|
130
|
+
else
|
|
131
|
+
super
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# 3) if there is a requester, then we do the job (assuming bin/alf)
|
|
135
|
+
# with the renderer to use. Otherwise, we simply return built operator
|
|
136
|
+
if operator && requester
|
|
137
|
+
renderer = self.renderer ||= Renderer::Rash.new
|
|
138
|
+
renderer.pipe(operator, environment).execute($stdout)
|
|
139
|
+
else
|
|
140
|
+
operator
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
end # class Main
|
|
145
|
+
end # module Command
|
|
146
|
+
end # module Alf
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Command
|
|
3
|
+
class Show < Alf::Command()
|
|
4
|
+
include Command
|
|
5
|
+
|
|
6
|
+
options do |opt|
|
|
7
|
+
@renderer = nil
|
|
8
|
+
Renderer.each_renderer do |name,descr,clazz|
|
|
9
|
+
opt.on("--#{name}", "Render output #{descr}"){
|
|
10
|
+
@renderer = clazz.new
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def execute(args)
|
|
16
|
+
requester.renderer = (@renderer || requester.renderer || Text::Renderer.new)
|
|
17
|
+
args = [ stdin_reader ] if args.empty?
|
|
18
|
+
args.first
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def stdin_reader
|
|
24
|
+
if requester && requester.respond_to?(:stdin_reader)
|
|
25
|
+
requester.stdin_reader
|
|
26
|
+
else
|
|
27
|
+
Reader.coerce($stdin)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end # class Show
|
|
32
|
+
end # module Command
|
|
33
|
+
end # module Alf
|