antelope 0.3.2 → 0.4.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 (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