antelope 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +25 -25
  3. data/.rspec +3 -3
  4. data/.travis.yml +10 -10
  5. data/.yardopts +7 -7
  6. data/CONTRIBUTING.md +50 -38
  7. data/GENERATORS.md +180 -124
  8. data/Gemfile +7 -7
  9. data/LICENSE.txt +22 -22
  10. data/README.md +240 -104
  11. data/Rakefile +2 -2
  12. data/TODO.md +58 -58
  13. data/antelope.gemspec +29 -28
  14. data/bin/antelope +7 -7
  15. data/examples/deterministic.ace +35 -35
  16. data/examples/example.ace +52 -51
  17. data/examples/example.ace.err +192 -192
  18. data/examples/example.ace.inf +432 -432
  19. data/examples/example.ate +70 -70
  20. data/examples/example.ate.err +192 -192
  21. data/examples/example.ate.inf +432 -432
  22. data/examples/liquidscript.ace +233 -233
  23. data/examples/simple.ace +22 -22
  24. data/lib/antelope/ace/compiler.rb +334 -334
  25. data/lib/antelope/ace/errors.rb +30 -30
  26. data/lib/antelope/ace/scanner/argument.rb +57 -57
  27. data/lib/antelope/ace/scanner/first.rb +89 -89
  28. data/lib/antelope/ace/scanner/second.rb +178 -178
  29. data/lib/antelope/ace/scanner/third.rb +27 -27
  30. data/lib/antelope/ace/scanner.rb +144 -144
  31. data/lib/antelope/ace.rb +47 -47
  32. data/lib/antelope/cli.rb +60 -60
  33. data/lib/antelope/errors.rb +25 -25
  34. data/lib/antelope/generation/constructor/first.rb +86 -86
  35. data/lib/antelope/generation/constructor/follow.rb +105 -105
  36. data/lib/antelope/generation/constructor/nullable.rb +64 -64
  37. data/lib/antelope/generation/constructor.rb +127 -127
  38. data/lib/antelope/generation/errors.rb +17 -17
  39. data/lib/antelope/generation/null.rb +13 -13
  40. data/lib/antelope/generation/recognizer/rule.rb +216 -216
  41. data/lib/antelope/generation/recognizer/state.rb +129 -129
  42. data/lib/antelope/generation/recognizer.rb +177 -177
  43. data/lib/antelope/generation/tableizer.rb +176 -176
  44. data/lib/antelope/generation.rb +15 -15
  45. data/lib/antelope/generator/base/coerce.rb +115 -0
  46. data/lib/antelope/generator/base/extra.rb +50 -0
  47. data/lib/antelope/generator/base.rb +134 -264
  48. data/lib/antelope/generator/c.rb +11 -11
  49. data/lib/antelope/generator/c_header.rb +105 -105
  50. data/lib/antelope/generator/c_source.rb +39 -39
  51. data/lib/antelope/generator/error.rb +34 -34
  52. data/lib/antelope/generator/group.rb +60 -57
  53. data/lib/antelope/generator/html.rb +51 -51
  54. data/lib/antelope/generator/info.rb +47 -47
  55. data/lib/antelope/generator/null.rb +18 -18
  56. data/lib/antelope/generator/output.rb +17 -17
  57. data/lib/antelope/generator/ruby.rb +112 -79
  58. data/lib/antelope/generator/templates/c_header.ant +36 -36
  59. data/lib/antelope/generator/templates/c_source.ant +202 -202
  60. data/lib/antelope/generator/templates/error.erb +40 -0
  61. data/lib/antelope/generator/templates/html/antelope.css +53 -1
  62. data/lib/antelope/generator/templates/html/antelope.html +82 -1
  63. data/lib/antelope/generator/templates/html/antelope.js +9 -1
  64. data/lib/antelope/generator/templates/html/css.ant +53 -53
  65. data/lib/antelope/generator/templates/html/html.ant +82 -82
  66. data/lib/antelope/generator/templates/html/js.ant +9 -9
  67. data/lib/antelope/generator/templates/info.erb +61 -0
  68. data/lib/antelope/generator/templates/{ruby.ant → ruby.erb} +171 -178
  69. data/lib/antelope/generator.rb +62 -66
  70. data/lib/antelope/grammar/generation.rb +76 -76
  71. data/lib/antelope/grammar/loading.rb +84 -84
  72. data/lib/antelope/grammar/precedence.rb +59 -59
  73. data/lib/antelope/grammar/precedences.rb +64 -64
  74. data/lib/antelope/grammar/production.rb +56 -56
  75. data/lib/antelope/grammar/productions.rb +154 -154
  76. data/lib/antelope/grammar/symbols.rb +64 -64
  77. data/lib/antelope/grammar/token/epsilon.rb +23 -23
  78. data/lib/antelope/grammar/token/error.rb +24 -24
  79. data/lib/antelope/grammar/token/nonterminal.rb +15 -15
  80. data/lib/antelope/grammar/token/terminal.rb +15 -15
  81. data/lib/antelope/grammar/token.rb +231 -231
  82. data/lib/antelope/grammar.rb +68 -68
  83. data/lib/antelope/version.rb +6 -6
  84. data/lib/antelope.rb +18 -19
  85. data/optimizations.txt +42 -42
  86. data/spec/antelope/ace/compiler_spec.rb +60 -60
  87. data/spec/antelope/ace/scanner_spec.rb +27 -27
  88. data/spec/antelope/generation/constructor_spec.rb +131 -131
  89. data/spec/fixtures/simple.ace +22 -22
  90. data/spec/spec_helper.rb +39 -39
  91. data/spec/support/benchmark_helper.rb +5 -5
  92. data/spec/support/grammar_helper.rb +14 -14
  93. data/subl/Ace (Ruby).JSON-tmLanguage +94 -94
  94. data/subl/Ace (Ruby).tmLanguage +153 -153
  95. metadata +22 -11
  96. data/lib/antelope/generator/templates/error.ant +0 -34
  97. data/lib/antelope/generator/templates/info.ant +0 -53
  98. data/lib/antelope/template/compiler.rb +0 -78
  99. data/lib/antelope/template/errors.rb +0 -9
  100. data/lib/antelope/template/scanner.rb +0 -109
  101. data/lib/antelope/template.rb +0 -64
  102. data/spec/antelope/template_spec.rb +0 -50
@@ -1,178 +1,171 @@
1
-
2
- # This file assumes that the output of the generator will be placed
3
- # within a module or a class. However, the module/class requires a
4
- # `type` method, which takes a terminal and gives its type, as a
5
- # symbol. These types should line up with the terminals that were
6
- # defined in the original grammar.
7
-
8
- # The actions to take during parsing. In every state, there are a
9
- # set of acceptable peek tokens; this table tells the parser what
10
- # to do on each acceptable peek token. The possible actions include
11
- # `:accept`, `:reduce`, and `:state`; `:accept` means to accept the
12
- # input and return the value of the pasing. `:reduce` means to
13
- # reduce the top of the stack into a given nonterminal. `:state`
14
- # means to transition to another state.
15
- #
16
- # @return [Array<Hash<(Symbol, Array<(Symbol, Numeric)>)>>]
17
- ACTION_TABLE = {{= generate_action_table }}.freeze
18
-
19
- # The default action that is taken for most reductions.
20
- #
21
- # @return [Proc]
22
- DEFAULT_PROC = proc { |_| _ }
23
- # A list of all of the productions. Only includes the left-hand side,
24
- # the number of tokens on the right-hand side, and the block to call
25
- # on reduction.
26
- #
27
- # @return [Array<Array<(Symbol, Numeric, Proc)>>]
28
- PRODUCTIONS = {{= generate_productions_list }}.freeze
29
-
30
- # Runs the parser.
31
- #
32
- # @param input [Array] the input to run the parser over.
33
- # @return [Object] the result of the accept.
34
- def parse(input)
35
- stack = []
36
- stack.push([nil, 0])
37
- last = nil
38
- input = input.to_a.dup
39
-
40
- until stack.empty? do
41
- last = parse_action(stack, input)
42
- end
43
-
44
- last
45
-
46
- end
47
-
48
- # Actually performs the parsing action on the given stack on input.
49
- # If you want to implement a push parser, than messing with this
50
- # method is probably the way to go.
51
- #
52
- # @param stack [Array<Array<(Object, Numeric)>>] the stack of the
53
- # parser. The actual order of the stack is important.
54
- # @param input [Array<Object>] the input to run the parser over.
55
- # The elements of this may be passed to the `type` method.
56
- # @return [Object] the result of the last accepting reduction.
57
- def parse_action(stack, input)
58
- last = nil
59
- peek_token = if input.empty?
60
- :$end
61
- else
62
- type(input.first)
63
- end
64
-
65
- action = ACTION_TABLE[stack.last.last].fetch(peek_token) do
66
- ACTION_TABLE[stack.last.last].fetch(:$default)
67
- end
68
-
69
- case action.first
70
- when :accept
71
- production = PRODUCTIONS[action.last]
72
- last = stack.pop(production[1]).first.first
73
- stack.pop
74
- when :reduce
75
- production = PRODUCTIONS[action.last]
76
- removing = stack.pop(production[1])
77
- value = instance_exec(removing.map(&:first), &production[2])
78
- goto = ACTION_TABLE[stack.last.last][production[0]]
79
- stack.push([value, goto.last])
80
- when :state
81
- stack.push([input.shift, action.last])
82
- else
83
- raise NotImplementedError, "Unknown action #{action.first}"
84
- end
85
-
86
- last
87
-
88
- rescue KeyError => e
89
- peek = input.first
90
-
91
- if handle_error({
92
- :stack => stack,
93
- :peek => peek_token,
94
- :remaining => input,
95
- :error => e,
96
- :line => line_of(peek),
97
- :column => column_of(peek),
98
- :expected => ACTION_TABLE[stack.last.last].keys
99
- })
100
- retry
101
- else
102
- raise
103
- end
104
- end
105
-
106
- private
107
-
108
- def line_of(peek)
109
- if peek.respond_to?(:line)
110
- peek.line
111
- else
112
- 0
113
- end
114
- end
115
-
116
- def column_of(peek)
117
- if peek.respond_to?(:column)
118
- peek.column
119
- else
120
- 0
121
- end
122
- end
123
-
124
- {{ if define_own_handler? }}
125
- def handle_error(data, _ = false)
126
- {{ if panic_mode? }}
127
- if _ || data[:peek] == :$end # we can't recover if
128
- # we're at the end
129
- {{ end }}
130
- raise {{= error_class }},
131
- "Unexpected token #{data[:peek]} on line #{data[:line]}, " \
132
- "column #{data[:column]}; expected one of " \
133
- "#{data[:expected].join(', ')}",
134
- data[:error].backtrace
135
- {{ if panic_mode? }}
136
- end
137
-
138
- new_peek = :$error
139
- acceptable_state = false
140
- state = nil
141
-
142
- until data[:stack].empty? or acceptable_state
143
- state = data[:stack].last.last
144
-
145
- if ACTION_TABLE[state].key?(new_peek)
146
- acceptable_state = true
147
- else
148
- data[:stack].pop # discard
149
- end
150
- end
151
-
152
- return handle_error(data, true) unless acceptable_state
153
-
154
- action = ACTION_TABLE[state][new_peek]
155
- lookaheads = nil
156
-
157
- until lookaheads
158
- if action[0] == :state
159
- lookaheads = ACTION_TABLE[action.last].keys
160
- elsif action[0] == :reduce
161
- rule = PRODUCTIONS[action.last]
162
- action = ACTION_TABLE[stack[-rule[1]].last][rule[0]]
163
- end
164
- end
165
-
166
- begin
167
- until lookaheads.include?(data[:remaining][0].peek)
168
- data[:remaining].next
169
- end
170
- rescue StopIteration
171
- end
172
-
173
- data[:remaining].unshift([new_peek, data[:error]])
174
- true
175
-
176
- {{ end }}
177
- end
178
- {{ end }}
1
+ # This file assumes that the output of the generator will be placed
2
+ # within a module or a class. However, the module/class requires a
3
+ # `type` method, which takes a terminal and gives its type, as a
4
+ # symbol. These types should line up with the terminals that were
5
+ # defined in the original grammar.
6
+
7
+ # The actions to take during parsing. In every state, there are a
8
+ # set of acceptable peek tokens; this table tells the parser what
9
+ # to do on each acceptable peek token. The possible actions include
10
+ # `:accept`, `:reduce`, and `:state`; `:accept` means to accept the
11
+ # input and return the value of the pasing. `:reduce` means to
12
+ # reduce the top of the stack into a given nonterminal. `:state`
13
+ # means to transition to another state.
14
+ #
15
+ # @return [Array<Hash<(Symbol, Array<(Symbol, Numeric)>)>>]
16
+ ACTION_TABLE = <%= generate_action_table %>.freeze
17
+
18
+ # The default action that is taken for most reductions.
19
+ #
20
+ # @return [Proc]
21
+ DEFAULT_PROC = proc { |_| _ }
22
+ # A list of all of the productions. Only includes the left-hand side,
23
+ # the number of tokens on the right-hand side, and the block to call
24
+ # on reduction.
25
+ #
26
+ # @return [Array<Array<(Symbol, Numeric, Proc)>>]
27
+ PRODUCTIONS = <%= generate_productions_list %>.freeze
28
+
29
+ # Runs the parser.
30
+ #
31
+ # @param input [Array] the input to run the parser over.
32
+ # @return [Object] the result of the accept.
33
+ def parse(input)
34
+ stack = []
35
+ stack.push([nil, 0])
36
+ input = input.to_a.dup
37
+
38
+ parse_action(stack, input) until stack.empty?
39
+ end
40
+
41
+ # Actually performs the parsing action on the given stack on input.
42
+ # If you want to implement a push parser, than messing with this
43
+ # method is probably the way to go.
44
+ #
45
+ # @param stack [Array<Array<(Object, Numeric)>>] the stack of the
46
+ # parser. The actual order of the stack is important.
47
+ # @param input [Array<Object>] the input to run the parser over.
48
+ # The elements of this may be passed to the `type` method.
49
+ # @return [Object] the result of the last accepting reduction.
50
+ def parse_action(stack, input)
51
+ last = nil
52
+ peek_token = if input.empty?
53
+ :$end
54
+ else
55
+ type(input.first)
56
+ end
57
+
58
+ action = ACTION_TABLE[stack.last.last].fetch(peek_token) do
59
+ ACTION_TABLE[stack.last.last].fetch(:$default)
60
+ end
61
+
62
+ case action.first
63
+ when :accept
64
+ production = PRODUCTIONS[action.last]
65
+ last = stack.pop(production[1]).first.first
66
+ stack.pop
67
+ when :reduce
68
+ production = PRODUCTIONS[action.last]
69
+ removing = stack.pop(production[1])
70
+ value = instance_exec(removing.map(&:first), &production[2])
71
+ goto = ACTION_TABLE[stack.last.last][production[0]]
72
+ stack.push([value, goto.last])
73
+ when :state
74
+ stack.push([input.shift, action.last])
75
+ else
76
+ raise NotImplementedError, "Unknown action #{action.first}"
77
+ end
78
+
79
+ last
80
+
81
+ rescue KeyError => e
82
+ peek = input.first
83
+
84
+ if handle_error(
85
+ stack: stack,
86
+ peek: peek_token,
87
+ remaining: input,
88
+ error: e,
89
+ line: line_of(peek),
90
+ column: column_of(peek),
91
+ expected: ACTION_TABLE[stack.last.last].keys
92
+ )
93
+ retry
94
+ else
95
+ raise
96
+ end
97
+ end
98
+
99
+ private
100
+
101
+ def line_of(peek)
102
+ if peek.respond_to?(:line)
103
+ peek.line
104
+ else
105
+ 0
106
+ end
107
+ end
108
+
109
+ def column_of(peek)
110
+ if peek.respond_to?(:column)
111
+ peek.column
112
+ else
113
+ 0
114
+ end
115
+ end
116
+
117
+ <% if define_own_handler? -%>
118
+ def handle_error(data, _ = false)
119
+ <%- if panic_mode? -%>
120
+ if _ || data[:peek] == :$end # we can't recover if
121
+ # we're at the end
122
+ <%- end -%>
123
+ raise <%= error_class %>,
124
+ "Unexpected token #{data[:peek]} on line #{data[:line]}, " \
125
+ "column #{data[:column]}; expected one of " \
126
+ "#{data[:expected].join(', ')}",
127
+ data[:error].backtrace
128
+ <%- if panic_mode? -%>
129
+ end
130
+
131
+ new_peek = :$error
132
+ acceptable_state = false
133
+ state = nil
134
+
135
+ until data[:stack].empty? or acceptable_state
136
+ state = data[:stack].last.last
137
+
138
+ if ACTION_TABLE[state].key?(new_peek)
139
+ acceptable_state = true
140
+ else
141
+ data[:stack].pop # discard
142
+ end
143
+ end
144
+
145
+ return handle_error(data, true) unless acceptable_state
146
+
147
+ action = ACTION_TABLE[state][new_peek]
148
+ lookaheads = nil
149
+
150
+ until lookaheads
151
+ if action[0] == :state
152
+ lookaheads = ACTION_TABLE[action.last].keys
153
+ elsif action[0] == :reduce
154
+ rule = PRODUCTIONS[action.last]
155
+ action = ACTION_TABLE[stack[-rule[1]].last][rule[0]]
156
+ end
157
+ end
158
+
159
+ begin
160
+ until lookaheads.include?(data[:remaining][0].peek)
161
+ data[:remaining].next
162
+ end
163
+ rescue StopIteration
164
+ end
165
+
166
+ data[:remaining].unshift([new_peek, data[:error]])
167
+ true
168
+
169
+ <%- end -%>
170
+ end
171
+ <%- end -%>
@@ -1,66 +1,62 @@
1
- # encoding: utf-8
2
- require "erb"
3
- require "pathname"
4
-
5
- module Antelope
6
-
7
- # Contains the classes that generate parsers. This contains a
8
- # registery of all of the generators available to antelope.
9
- module Generator
10
-
11
- # Returns a hash of all of the generators registered within this
12
- # module. If a generator is accessed that does not exist on the
13
- # hash, it by default returns the {Generator::Null} class.
14
- #
15
- # @return [Hash<(Symbol, String) => Generator::Base>]
16
- def generators
17
- @_generators ||= Hash.new { |h, k| h[k] = Generator::Null }
18
- end
19
- # Returns a hash of all of the directives that are available in
20
- # the generators of this module.
21
- #
22
- # @see .generators
23
- # @return [Hash]
24
- def directives
25
- generators.values.map(&:directives).
26
- inject({}, :merge)
27
- end
28
-
29
- # Registers a generator with the given names. If multiple names
30
- # are given, they are assigned the generator as a value in the
31
- # {#generators} hash; otherwise, the one name is assigned the
32
- # generator as a value.
33
- #
34
- # @param generator [Generator::Base] the generator class to
35
- # associate the key with.
36
- # @param name [String, Symbol] a name to associate the generator
37
- # with.
38
- def register_generator(generator, *names)
39
- names = [names].flatten
40
- raise ArgumentError,
41
- "Requires at least one name" unless names.any?
42
- raise ArgumentError,
43
- "All name values must be a Symbol or string" unless names.
44
- all? {|_| [Symbol, String].include?(_.class) }
45
-
46
- names.each do |name|
47
- generators[name.to_s.downcase] = generator
48
- end
49
- end
50
-
51
- extend self
52
-
53
- end
54
- end
55
-
56
- require "antelope/generator/base"
57
- require "antelope/generator/group"
58
- require "antelope/generator/info"
59
- require "antelope/generator/error"
60
- require "antelope/generator/output"
61
- require "antelope/generator/html"
62
- require "antelope/generator/ruby"
63
- require "antelope/generator/null"
64
- require "antelope/generator/c_header"
65
- require "antelope/generator/c_source"
66
- require "antelope/generator/c"
1
+ # encoding: utf-8
2
+ require "erb"
3
+ require "pathname"
4
+
5
+ module Antelope
6
+
7
+ # Contains the classes that generate parsers. This contains a
8
+ # registery of all of the generators available to antelope.
9
+ module Generator
10
+
11
+ # Returns a hash of all of the generators registered within this
12
+ # module. If a generator is accessed that does not exist on the
13
+ # hash, it by default returns the {Generator::Null} class.
14
+ #
15
+ # @return [Hash<(Symbol, String) => Generator::Base>]
16
+ def generators
17
+ @_generators ||= Hash.new { |h, k| h[k] = Generator::Null }
18
+ end
19
+ # Returns a hash of all of the directives that are available in
20
+ # the generators of this module.
21
+ #
22
+ # @see .generators
23
+ # @return [Hash]
24
+ def directives
25
+ generators.values.map(&:directives).
26
+ inject({}, :merge)
27
+ end
28
+
29
+ # Registers a generator with the given names. If multiple names
30
+ # are given, they are assigned the generator as a value in the
31
+ # {#generators} hash; otherwise, the one name is assigned the
32
+ # generator as a value.
33
+ #
34
+ # @param generator [Generator::Base] the generator class to
35
+ # associate the key with.
36
+ # @param name [String, Symbol] a name to associate the generator
37
+ # with.
38
+ def register_generator(generator, *names)
39
+ names = [names].flatten
40
+ raise ArgumentError,
41
+ "Requires at least one name" unless names.any?
42
+ raise ArgumentError,
43
+ "All name values must be a Symbol or string" unless names.
44
+ all? {|_| [Symbol, String].include?(_.class) }
45
+
46
+ names.each do |name|
47
+ generators[name.to_s.downcase] = generator
48
+ end
49
+ end
50
+
51
+ extend self
52
+
53
+ end
54
+ end
55
+
56
+ require "antelope/generator/base"
57
+ require "antelope/generator/group"
58
+ require "antelope/generator/info"
59
+ require "antelope/generator/error"
60
+ require "antelope/generator/output"
61
+ require "antelope/generator/ruby"
62
+ require "antelope/generator/null"
@@ -1,76 +1,76 @@
1
- # encoding: utf-8
2
-
3
- module Antelope
4
- class Grammar
5
- # The default modifiers for generation. It's not really
6
- # recommended to (heh) modify this; however, adding your own
7
- # modifier is always acceptable.
8
- DEFAULT_MODIFIERS = [
9
- [:recognizer, Generation::Recognizer ],
10
- [:constructor, Generation::Constructor],
11
- [:tableizer, Generation::Tableizer ]
12
- ].freeze
13
-
14
- # Handles the generation of output for the grammar.
15
- module Generation
16
- # Generates the output. First, it runs through every given
17
- # modifier, and instintates it. It then calls every modifier,
18
- # turns it into a hash, and passes that hash to each of the
19
- # given generators.
20
- #
21
- # @param options [Hash] options.
22
- # @param generators [Array<Generator>] a list of generators
23
- # to use in generation.
24
- # @param modifiers [Array<Array<(Symbol, #call)>>] a list of
25
- # modifiers to apply to the grammar.
26
- # @return [void]
27
- def generate(options = {},
28
- generators = :guess,
29
- modifiers = DEFAULT_MODIFIERS)
30
- mods = modifiers.map(&:last)
31
- .map { |x| x.new(self) }
32
- mods.each do |mod|
33
- puts "Running mod #{mod.class}..." if options[:verbose]
34
- mod.call
35
- end
36
- hash = Hash[modifiers.map(&:first).zip(mods)]
37
- # This is when we'd generate
38
-
39
- find_generators(generators, options).each do |gen|
40
- puts "Running generator #{gen}..." if options[:verbose]
41
- gen.new(self, hash).generate
42
- end
43
- end
44
-
45
- private
46
-
47
- # Find the corresponding generators. If the first argument
48
- # isn't `:guess`, it returns the first argument. Otherwise,
49
- # it tries to "intelligently guess" by checking the type from
50
- # the options _or_ the compiler. If it is unable to find the
51
- # type, it will raise a {NoTypeError}.
52
- #
53
- # @raise [NoTypeError] if it could not determine the type of
54
- # the generator.
55
- # @param generators [Symbol, Array<Generator>]
56
- # @param options [Hash]
57
- # @return [Array<Generator>]
58
- def find_generators(generators, options)
59
- return generators unless generators == :guess
60
-
61
- generators = [Generator::Output]
62
-
63
- # command line precedence...
64
- type = options[:type] || options['type'] ||
65
- compiler.options.fetch(:type)
66
-
67
- generators << Generator.generators.fetch(type.to_s)
68
-
69
- generators
70
-
71
- rescue KeyError
72
- raise NoTypeError, "Undefined type #{type}"
73
- end
74
- end
75
- end
76
- end
1
+ # encoding: utf-8
2
+
3
+ module Antelope
4
+ class Grammar
5
+ # The default modifiers for generation. It's not really
6
+ # recommended to (heh) modify this; however, adding your own
7
+ # modifier is always acceptable.
8
+ DEFAULT_MODIFIERS = [
9
+ [:recognizer, Generation::Recognizer ],
10
+ [:constructor, Generation::Constructor],
11
+ [:tableizer, Generation::Tableizer ]
12
+ ].freeze
13
+
14
+ # Handles the generation of output for the grammar.
15
+ module Generation
16
+ # Generates the output. First, it runs through every given
17
+ # modifier, and instintates it. It then calls every modifier,
18
+ # turns it into a hash, and passes that hash to each of the
19
+ # given generators.
20
+ #
21
+ # @param options [Hash] options.
22
+ # @param generators [Array<Generator>] a list of generators
23
+ # to use in generation.
24
+ # @param modifiers [Array<Array<(Symbol, #call)>>] a list of
25
+ # modifiers to apply to the grammar.
26
+ # @return [void]
27
+ def generate(options = {},
28
+ generators = :guess,
29
+ modifiers = DEFAULT_MODIFIERS)
30
+ mods = modifiers.map(&:last)
31
+ .map { |x| x.new(self) }
32
+ mods.each do |mod|
33
+ puts "Running mod #{mod.class}..." if options[:verbose]
34
+ mod.call
35
+ end
36
+ hash = Hash[modifiers.map(&:first).zip(mods)]
37
+ # This is when we'd generate
38
+
39
+ find_generators(generators, options).each do |gen|
40
+ puts "Running generator #{gen}..." if options[:verbose]
41
+ gen.new(self, hash).generate
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ # Find the corresponding generators. If the first argument
48
+ # isn't `:guess`, it returns the first argument. Otherwise,
49
+ # it tries to "intelligently guess" by checking the type from
50
+ # the options _or_ the compiler. If it is unable to find the
51
+ # type, it will raise a {NoTypeError}.
52
+ #
53
+ # @raise [NoTypeError] if it could not determine the type of
54
+ # the generator.
55
+ # @param generators [Symbol, Array<Generator>]
56
+ # @param options [Hash]
57
+ # @return [Array<Generator>]
58
+ def find_generators(generators, options)
59
+ return generators unless generators == :guess
60
+
61
+ generators = [Generator::Output]
62
+
63
+ # command line precedence...
64
+ type = options[:type] || options['type'] ||
65
+ compiler.options.fetch(:type)
66
+
67
+ generators << Generator.generators.fetch(type.to_s)
68
+
69
+ generators
70
+
71
+ rescue KeyError
72
+ raise NoTypeError, "Undefined type #{type}"
73
+ end
74
+ end
75
+ end
76
+ end