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.
Files changed (270) hide show
  1. data/CHANGELOG.md +255 -129
  2. data/Gemfile +31 -1
  3. data/Gemfile.lock +17 -20
  4. data/LICENCE.md +1 -1
  5. data/Manifest.txt +2 -0
  6. data/README.md +37 -43
  7. data/TODO.md +1 -1
  8. data/alf.gemspec +10 -7
  9. data/alf.noespec +24 -13
  10. data/bin/alf +2 -2
  11. data/doc/commands/exec.md +16 -0
  12. data/doc/commands/help.md +11 -0
  13. data/doc/commands/main.md +33 -0
  14. data/doc/commands/show.md +19 -0
  15. data/doc/operators/non_relational/autonum.md +23 -0
  16. data/doc/operators/non_relational/clip.md +31 -0
  17. data/doc/operators/non_relational/coerce.md +15 -0
  18. data/doc/operators/non_relational/compact.md +20 -0
  19. data/doc/operators/non_relational/defaults.md +32 -0
  20. data/doc/operators/non_relational/generator.md +20 -0
  21. data/doc/operators/non_relational/sort.md +24 -0
  22. data/doc/operators/relational/extend.md +18 -0
  23. data/doc/operators/relational/group.md +27 -0
  24. data/doc/operators/relational/intersect.md +13 -0
  25. data/doc/operators/relational/join.md +27 -0
  26. data/doc/operators/relational/matching.md +20 -0
  27. data/doc/operators/relational/minus.md +12 -0
  28. data/doc/operators/relational/not-matching.md +20 -0
  29. data/doc/operators/relational/project.md +28 -0
  30. data/doc/operators/relational/quota.md +21 -0
  31. data/doc/operators/relational/rank.md +27 -0
  32. data/doc/operators/relational/rename.md +17 -0
  33. data/doc/operators/relational/restrict.md +25 -0
  34. data/doc/operators/relational/summarize.md +25 -0
  35. data/doc/operators/relational/ungroup.md +20 -0
  36. data/doc/operators/relational/union.md +14 -0
  37. data/doc/operators/relational/unwrap.md +20 -0
  38. data/doc/operators/relational/wrap.md +24 -0
  39. data/examples/csv/suppliers.csv +6 -0
  40. data/examples/logs/access.log +1000 -0
  41. data/examples/logs/combined.alf +2 -0
  42. data/examples/logs/hits.alf +14 -0
  43. data/examples/logs/not_found.alf +7 -0
  44. data/examples/logs/robots-cheating.alf +11 -0
  45. data/examples/logs/robots.alf +8 -0
  46. data/examples/northwind/customers.csv +92 -0
  47. data/examples/northwind/northwind.db +0 -0
  48. data/examples/northwind/orders.csv +831 -0
  49. data/examples/operators/clip.alf +1 -1
  50. data/examples/operators/database.alf +5 -6
  51. data/examples/operators/defaults.alf +1 -1
  52. data/examples/operators/group.alf +1 -1
  53. data/examples/operators/project.alf +2 -1
  54. data/examples/operators/pseudo-with.alf +2 -2
  55. data/examples/operators/quota.alf +2 -2
  56. data/examples/operators/summarize.alf +2 -2
  57. data/lib/alf/aggregator/aggregators.rb +77 -0
  58. data/lib/alf/aggregator/base.rb +95 -0
  59. data/lib/alf/aggregator/class_methods.rb +57 -0
  60. data/lib/alf/buffer/sorted.rb +48 -0
  61. data/lib/alf/command/class_methods.rb +27 -0
  62. data/lib/alf/command/doc_manager.rb +72 -0
  63. data/lib/alf/command/exec.rb +12 -0
  64. data/lib/alf/command/help.rb +31 -0
  65. data/lib/alf/command/main.rb +146 -0
  66. data/lib/alf/command/show.rb +33 -0
  67. data/lib/alf/environment/base.rb +37 -0
  68. data/lib/alf/environment/class_methods.rb +93 -0
  69. data/lib/alf/environment/explicit.rb +38 -0
  70. data/lib/alf/environment/folder.rb +62 -0
  71. data/lib/alf/extra/csv.rb +104 -0
  72. data/lib/alf/extra/logs.rb +100 -0
  73. data/lib/alf/extra/sequel.rb +77 -0
  74. data/lib/alf/{yaml.rb → extra/yaml.rb} +0 -0
  75. data/lib/alf/extra.rb +5 -0
  76. data/lib/alf/iterator/base.rb +38 -0
  77. data/lib/alf/iterator/class_methods.rb +22 -0
  78. data/lib/alf/iterator/proxy.rb +33 -0
  79. data/lib/alf/lispy/instance_methods.rb +157 -0
  80. data/lib/alf/operator/base.rb +74 -0
  81. data/lib/alf/operator/binary.rb +32 -0
  82. data/lib/alf/operator/cesure.rb +45 -0
  83. data/lib/alf/operator/class_methods.rb +132 -0
  84. data/lib/alf/operator/experimental.rb +9 -0
  85. data/lib/alf/operator/non_relational/autonum.rb +24 -0
  86. data/lib/alf/operator/non_relational/clip.rb +20 -0
  87. data/lib/alf/operator/non_relational/coerce.rb +21 -0
  88. data/lib/alf/operator/non_relational/compact.rb +62 -0
  89. data/lib/alf/operator/non_relational/defaults.rb +25 -0
  90. data/lib/alf/operator/non_relational/generator.rb +38 -0
  91. data/lib/alf/operator/non_relational/sort.rb +23 -0
  92. data/lib/alf/operator/nullary.rb +20 -0
  93. data/lib/alf/operator/relational/extend.rb +24 -0
  94. data/lib/alf/operator/relational/group.rb +32 -0
  95. data/lib/alf/operator/relational/intersect.rb +37 -0
  96. data/lib/alf/operator/relational/join.rb +106 -0
  97. data/lib/alf/operator/relational/matching.rb +45 -0
  98. data/lib/alf/operator/relational/minus.rb +37 -0
  99. data/lib/alf/operator/relational/not_matching.rb +45 -0
  100. data/lib/alf/operator/relational/project.rb +22 -0
  101. data/lib/alf/operator/relational/quota.rb +51 -0
  102. data/lib/alf/operator/relational/rank.rb +55 -0
  103. data/lib/alf/operator/relational/rename.rb +19 -0
  104. data/lib/alf/operator/relational/restrict.rb +20 -0
  105. data/lib/alf/operator/relational/summarize.rb +83 -0
  106. data/lib/alf/operator/relational/ungroup.rb +25 -0
  107. data/lib/alf/operator/relational/union.rb +32 -0
  108. data/lib/alf/operator/relational/unwrap.rb +21 -0
  109. data/lib/alf/operator/relational/wrap.rb +22 -0
  110. data/lib/alf/operator/shortcut.rb +53 -0
  111. data/lib/alf/operator/signature.rb +262 -0
  112. data/lib/alf/operator/transform.rb +27 -0
  113. data/lib/alf/operator/unary.rb +38 -0
  114. data/lib/alf/reader/alf_file.rb +24 -0
  115. data/lib/alf/reader/base.rb +119 -0
  116. data/lib/alf/reader/class_methods.rb +82 -0
  117. data/lib/alf/reader/rash.rb +28 -0
  118. data/lib/alf/relation/class_methods.rb +37 -0
  119. data/lib/alf/relation/instance_methods.rb +127 -0
  120. data/lib/alf/renderer/base.rb +72 -0
  121. data/lib/alf/renderer/class_methods.rb +58 -0
  122. data/lib/alf/renderer/rash.rb +19 -0
  123. data/lib/alf/{text.rb → renderer/text.rb} +1 -1
  124. data/lib/alf/tools/coerce.rb +14 -0
  125. data/lib/alf/tools/miscellaneous.rb +77 -0
  126. data/lib/alf/tools/to_lispy.rb +99 -0
  127. data/lib/alf/tools/to_ruby_literal.rb +14 -0
  128. data/lib/alf/tools/tuple_handle.rb +50 -0
  129. data/lib/alf/types/attr_list.rb +56 -0
  130. data/lib/alf/types/attr_name.rb +28 -0
  131. data/lib/alf/types/boolean.rb +12 -0
  132. data/lib/alf/types/heading.rb +96 -0
  133. data/lib/alf/types/ordering.rb +93 -0
  134. data/lib/alf/types/renaming.rb +57 -0
  135. data/lib/alf/types/summarization.rb +76 -0
  136. data/lib/alf/types/tuple_computation.rb +61 -0
  137. data/lib/alf/types/tuple_expression.rb +61 -0
  138. data/lib/alf/types/tuple_predicate.rb +49 -0
  139. data/lib/alf/version.rb +2 -2
  140. data/lib/alf.rb +193 -3714
  141. data/spec/integration/__database__/group.alf +1 -1
  142. data/spec/integration/__database__/suppliers_csv.csv +6 -0
  143. data/spec/integration/command/alf/alf.db +0 -0
  144. data/spec/integration/command/alf/alf_env_sqlite.cmd +1 -0
  145. data/spec/integration/command/alf/alf_env_sqlite.stdout +9 -0
  146. data/spec/integration/command/alf/alf_help.cmd +1 -0
  147. data/spec/integration/command/alf/alf_help.stdout +67 -0
  148. data/spec/integration/command/autonum/autonum_0.cmd +1 -1
  149. data/spec/integration/command/coerce/coerce_1.cmd +1 -0
  150. data/spec/integration/command/coerce/coerce_1.stdout +5 -0
  151. data/spec/integration/command/defaults/defaults_0.cmd +1 -1
  152. data/spec/integration/command/defaults/defaults_0.stdout +9 -9
  153. data/spec/integration/command/defaults/defaults_2.cmd +1 -0
  154. data/spec/integration/command/defaults/defaults_2.stdout +9 -0
  155. data/spec/integration/command/generator/generator_1.cmd +1 -0
  156. data/spec/integration/command/generator/generator_1.stdout +10 -0
  157. data/spec/integration/command/generator/generator_2.cmd +1 -0
  158. data/spec/integration/command/generator/generator_2.stdout +5 -0
  159. data/spec/integration/command/generator/generator_3.cmd +1 -0
  160. data/spec/integration/command/generator/generator_3.stdout +5 -0
  161. data/spec/integration/command/group/group_0.cmd +1 -1
  162. data/spec/integration/command/group/group_1.cmd +1 -1
  163. data/spec/integration/command/help/help_1.cmd +1 -0
  164. data/spec/integration/command/help/help_1.stdout +22 -0
  165. data/spec/integration/command/quota/quota_0.cmd +1 -1
  166. data/spec/integration/command/rank/rank_1.cmd +1 -1
  167. data/spec/integration/command/rank/rank_1.stdout +10 -10
  168. data/spec/integration/command/rank/rank_2.cmd +1 -1
  169. data/spec/integration/command/rank/rank_2.stdout +10 -10
  170. data/spec/integration/command/rank/rank_3.cmd +1 -1
  171. data/spec/integration/command/rank/rank_3.stdout +10 -10
  172. data/spec/integration/command/rank/rank_4.cmd +1 -1
  173. data/spec/integration/command/rank/rank_5.cmd +1 -1
  174. data/spec/integration/command/show/show_csv.cmd +1 -0
  175. data/spec/integration/command/show/show_csv.stdout +6 -0
  176. data/spec/integration/command/show/show_rash_2.cmd +1 -1
  177. data/spec/integration/command/show/show_rash_2.stdout +5 -5
  178. data/spec/integration/command/sort/sort_0.cmd +1 -1
  179. data/spec/integration/command/sort/sort_1.cmd +1 -1
  180. data/spec/integration/command/sort/sort_1.stdout +2 -2
  181. data/spec/integration/command/sort/sort_2.cmd +1 -0
  182. data/spec/integration/command/sort/sort_2.stdout +9 -0
  183. data/spec/integration/command/sort/sort_3.cmd +1 -0
  184. data/spec/integration/command/sort/sort_3.stdout +9 -0
  185. data/spec/integration/command/summarize/summarize_0.cmd +1 -1
  186. data/spec/integration/command/ungroup/ungroup_0.cmd +1 -1
  187. data/spec/integration/command/wrap/wrap_0.cmd +1 -1
  188. data/spec/integration/semantics/test_project.alf +5 -6
  189. data/spec/integration/semantics/test_rank.alf +16 -16
  190. data/spec/integration/test_command.rb +17 -6
  191. data/spec/integration/test_examples.rb +1 -1
  192. data/spec/regression/logs/apache_combined.log +5 -0
  193. data/spec/regression/logs/test_path_attribute.rb +25 -0
  194. data/spec/regression/relation/test_relation_allbut_all.rb +14 -0
  195. data/spec/shared/an_operator_class.rb +10 -5
  196. data/spec/spec_helper.rb +1 -7
  197. data/spec/unit/assumptions/test_set.rb +64 -0
  198. data/spec/unit/command/doc_manager/dynamic.md +1 -0
  199. data/spec/unit/command/doc_manager/example.md +1 -0
  200. data/spec/unit/command/doc_manager/example_1.txt +11 -0
  201. data/spec/unit/command/doc_manager/static.md +1 -0
  202. data/spec/unit/command/doc_manager/test_call.rb +49 -0
  203. data/spec/unit/csv/input.csv +3 -0
  204. data/spec/unit/csv/test_reader.rb +66 -0
  205. data/spec/unit/csv/test_renderer.rb +73 -0
  206. data/spec/unit/lispy/test_relation.rb +37 -0
  207. data/spec/unit/lispy/test_run.rb +40 -0
  208. data/spec/unit/lispy/test_tuple.rb +36 -0
  209. data/spec/unit/logs/apache_combined.log +5 -0
  210. data/spec/unit/logs/postgresql.log +29 -0
  211. data/spec/unit/logs/test_reader.rb +56 -0
  212. data/spec/unit/operator/non_relational/compact/{buffer_based.rb → test_buffer_based.rb} +0 -0
  213. data/spec/unit/operator/non_relational/test_clip.rb +1 -1
  214. data/spec/unit/operator/non_relational/test_coerce.rb +35 -0
  215. data/spec/unit/operator/non_relational/test_defaults.rb +15 -2
  216. data/spec/unit/operator/non_relational/test_generator.rb +78 -0
  217. data/spec/unit/operator/relational/join/test_hash_based.rb +4 -4
  218. data/spec/unit/operator/relational/matching/test_hash_based.rb +6 -6
  219. data/spec/unit/operator/relational/not_matching/test_hash_based.rb +4 -4
  220. data/spec/unit/operator/relational/summarize/test_hash_based.rb +10 -6
  221. data/spec/unit/operator/relational/summarize/test_sort_based.rb +18 -7
  222. data/spec/unit/operator/relational/test_group.rb +8 -8
  223. data/spec/unit/operator/relational/test_intersect.rb +3 -3
  224. data/spec/unit/operator/relational/test_minus.rb +3 -3
  225. data/spec/unit/operator/relational/test_project.rb +12 -2
  226. data/spec/unit/operator/relational/test_quota.rb +5 -6
  227. data/spec/unit/operator/relational/test_summarize.rb +9 -11
  228. data/spec/unit/operator/relational/test_union.rb +1 -1
  229. data/spec/unit/operator/relational/test_wrap.rb +1 -1
  230. data/spec/unit/operator/signature/test_collect_on.rb +45 -0
  231. data/spec/unit/operator/signature/test_initialize.rb +17 -0
  232. data/spec/unit/operator/signature/test_install.rb +56 -0
  233. data/spec/unit/operator/signature/test_option_parser.rb +36 -0
  234. data/spec/unit/operator/signature/test_parse_args.rb +60 -0
  235. data/spec/unit/operator/signature/test_parse_argv.rb +87 -0
  236. data/spec/unit/operator/signature/test_to_lispy.rb +102 -0
  237. data/spec/unit/operator/signature/test_to_shell.rb +103 -0
  238. data/spec/unit/operator/test_non_relational.rb +3 -1
  239. data/spec/unit/relation/test_relops.rb +20 -15
  240. data/spec/unit/sequel/alf.db +0 -0
  241. data/spec/unit/sequel/test_environment.rb +54 -0
  242. data/spec/unit/test_aggregator.rb +32 -22
  243. data/spec/unit/test_environment.rb +5 -0
  244. data/spec/unit/test_lispy.rb +4 -0
  245. data/spec/unit/test_relation.rb +5 -0
  246. data/spec/unit/text/test_cell.rb +6 -6
  247. data/spec/unit/text/test_row.rb +3 -3
  248. data/spec/unit/text/test_table.rb +6 -6
  249. data/spec/unit/tools/test_coalesce.rb +15 -0
  250. data/spec/unit/tools/test_coerce.rb +10 -0
  251. data/spec/unit/tools/test_to_lispy.rb +138 -0
  252. data/spec/unit/tools/test_to_ruby_literal.rb +10 -0
  253. data/spec/unit/tools/test_tuple_handle.rb +1 -59
  254. data/spec/unit/types/test_attr_list.rb +106 -0
  255. data/spec/unit/types/test_attr_name.rb +52 -0
  256. data/spec/unit/{test_heading.rb → types/test_heading.rb} +10 -0
  257. data/spec/unit/types/test_ordering.rb +127 -0
  258. data/spec/unit/types/test_renaming.rb +55 -0
  259. data/spec/unit/types/test_summarization.rb +63 -0
  260. data/spec/unit/types/test_tuple_computation.rb +60 -0
  261. data/spec/unit/types/test_tuple_expression.rb +64 -0
  262. data/spec/unit/types/test_tuple_predicate.rb +79 -0
  263. data/tasks/debug_mail.rake +1 -1
  264. data/tasks/debug_mail.txt +5 -0
  265. data/tasks/gh-pages.rake +63 -0
  266. metadata +325 -52
  267. data/spec/unit/operator/test_command_methods.rb +0 -38
  268. data/spec/unit/tools/test_ordering_key.rb +0 -94
  269. data/spec/unit/tools/test_parse_commandline_args.rb +0 -47
  270. data/spec/unit/tools/test_projection_key.rb +0 -83
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env alf
2
2
  (clip :suppliers, [:name, :city])
3
- (clip :suppliers, [:name, :city], true)
3
+ (clip :suppliers, [:name, :city], :allbut => true)
@@ -1,6 +1,5 @@
1
- Alf::Relation[{
2
- :suppliers => (dataset :suppliers),
3
- :parts => (dataset :parts),
4
- :cities => (dataset :cities),
5
- :supplies => (dataset :supplies)
6
- }]
1
+ Relation({
2
+ :suppliers => (Relation :suppliers),
3
+ :parts => (Relation :parts),
4
+ :supplies => (Relation :supplies),
5
+ })
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env alf
2
2
  (defaults :suppliers, :country => 'Belgium')
3
- (defaults :suppliers, {:country => 'Belgium'}, true)
3
+ (defaults :suppliers, {:country => 'Belgium'}, :strict => true)
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env alf
2
2
  (group :supplies, [:pid, :qty], :supplying)
3
- (group :supplies, [:sid], :supplying, true)
3
+ (group :supplies, [:sid], :supplying, :allbut => true)
@@ -1,2 +1,3 @@
1
1
  #!/usr/bin/env alf
2
- (project :suppliers, [:name, :city])
2
+ (project :suppliers, [:name, :city])
3
+ (project :suppliers, [:name, :city], :allbut => true)
@@ -3,5 +3,5 @@ with_countries = (join kept_suppliers, :cities)
3
3
  supplying = (join with_countries, :supplies)
4
4
  (summarize supplying,
5
5
  [:country],
6
- :which => Agg::group(:pid),
7
- :total => Agg::sum{ qty })
6
+ :which => collect{ pid },
7
+ :total => sum{ qty })
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env alf
2
2
  (quota :supplies, [:sid], [:qty],
3
- :position => Agg::count,
4
- :sum_qty => Agg::sum(:qty))
3
+ :position => count(),
4
+ :sum_qty => sum{ qty })
@@ -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 => Agg::sum(: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 => Agg::sum{ qty })
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,12 @@
1
+ module Alf
2
+ module Command
3
+ class Exec < Alf::Command()
4
+ include Command
5
+
6
+ def execute(args)
7
+ Reader.alf(args.first || $stdin, requester.environment)
8
+ end
9
+
10
+ end # class Exec
11
+ end # module Command
12
+ 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