antelope 0.2.0 → 0.2.2

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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +25 -23
  3. data/.rspec +3 -3
  4. data/.travis.yml +10 -9
  5. data/.yardopts +7 -7
  6. data/CONTRIBUTING.md +38 -38
  7. data/GENERATORS.md +124 -124
  8. data/Gemfile +7 -7
  9. data/LICENSE.txt +22 -22
  10. data/README.md +104 -104
  11. data/Rakefile +2 -2
  12. data/TODO.md +58 -58
  13. data/antelope.gemspec +28 -28
  14. data/bin/antelope +7 -7
  15. data/examples/deterministic.ace +35 -35
  16. data/examples/example.ace +51 -50
  17. data/examples/example.err +192 -0
  18. data/examples/{example.output → example.inf} +384 -385
  19. data/examples/liquidscript.ace +233 -162
  20. data/examples/simple.ace +22 -22
  21. data/lib/antelope/ace/compiler.rb +334 -334
  22. data/lib/antelope/ace/errors.rb +48 -48
  23. data/lib/antelope/ace/grammar/generation.rb +80 -80
  24. data/lib/antelope/ace/grammar/loading.rb +53 -53
  25. data/lib/antelope/ace/grammar/precedences.rb +68 -65
  26. data/lib/antelope/ace/grammar/productions.rb +156 -150
  27. data/lib/antelope/ace/grammar/symbols.rb +66 -66
  28. data/lib/antelope/ace/grammar.rb +69 -69
  29. data/lib/antelope/ace/precedence.rb +61 -61
  30. data/lib/antelope/ace/production.rb +57 -57
  31. data/lib/antelope/ace/scanner/argument.rb +57 -57
  32. data/lib/antelope/ace/scanner/first.rb +89 -89
  33. data/lib/antelope/ace/scanner/second.rb +177 -177
  34. data/lib/antelope/ace/scanner/third.rb +27 -27
  35. data/lib/antelope/ace/scanner.rb +134 -134
  36. data/lib/antelope/ace/token/epsilon.rb +24 -24
  37. data/lib/antelope/ace/token/error.rb +26 -26
  38. data/lib/antelope/ace/token/nonterminal.rb +17 -17
  39. data/lib/antelope/ace/token/terminal.rb +17 -17
  40. data/lib/antelope/ace/token.rb +238 -238
  41. data/lib/antelope/ace.rb +53 -53
  42. data/lib/antelope/cli.rb +55 -55
  43. data/lib/antelope/errors.rb +8 -8
  44. data/lib/antelope/generation/constructor/first.rb +88 -88
  45. data/lib/antelope/generation/constructor/follow.rb +103 -103
  46. data/lib/antelope/generation/constructor/nullable.rb +64 -64
  47. data/lib/antelope/generation/constructor.rb +126 -126
  48. data/lib/antelope/generation/errors.rb +17 -17
  49. data/lib/antelope/generation/null.rb +13 -13
  50. data/lib/antelope/generation/recognizer/rule.rb +216 -216
  51. data/lib/antelope/generation/recognizer/state.rb +130 -130
  52. data/lib/antelope/generation/recognizer.rb +180 -180
  53. data/lib/antelope/generation/tableizer.rb +175 -154
  54. data/lib/antelope/generation.rb +15 -15
  55. data/lib/antelope/generator/base.rb +264 -264
  56. data/lib/antelope/generator/c.rb +11 -11
  57. data/lib/antelope/generator/c_header.rb +105 -105
  58. data/lib/antelope/generator/c_source.rb +39 -39
  59. data/lib/antelope/generator/error.rb +34 -0
  60. data/lib/antelope/generator/group.rb +57 -57
  61. data/lib/antelope/generator/html.rb +51 -0
  62. data/lib/antelope/generator/info.rb +47 -0
  63. data/lib/antelope/generator/null.rb +18 -18
  64. data/lib/antelope/generator/output.rb +17 -49
  65. data/lib/antelope/generator/ruby.rb +79 -79
  66. data/lib/antelope/generator/templates/c_header.ant +36 -36
  67. data/lib/antelope/generator/templates/c_source.ant +202 -202
  68. data/lib/antelope/generator/templates/error.ant +33 -0
  69. data/lib/antelope/generator/templates/html/antelope.css +1 -0
  70. data/lib/antelope/generator/templates/html/antelope.html +1 -0
  71. data/lib/antelope/generator/templates/html/antelope.js +1 -0
  72. data/lib/antelope/generator/templates/html/css.ant +53 -0
  73. data/lib/antelope/generator/templates/html/html.ant +82 -0
  74. data/lib/antelope/generator/templates/html/js.ant +9 -0
  75. data/lib/antelope/generator/templates/info.ant +53 -0
  76. data/lib/antelope/generator/templates/ruby.ant +178 -146
  77. data/lib/antelope/generator.rb +66 -63
  78. data/lib/antelope/template/compiler.rb +78 -78
  79. data/lib/antelope/template/errors.rb +9 -9
  80. data/lib/antelope/template/scanner.rb +109 -109
  81. data/lib/antelope/template.rb +65 -60
  82. data/lib/antelope/version.rb +6 -6
  83. data/lib/antelope.rb +13 -13
  84. data/optimizations.txt +42 -0
  85. data/spec/antelope/ace/compiler_spec.rb +60 -60
  86. data/spec/antelope/ace/scanner_spec.rb +27 -27
  87. data/spec/antelope/constructor_spec.rb +133 -136
  88. data/spec/antelope/template_spec.rb +50 -49
  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 +15 -15
  93. data/subl/Ace (Ruby).JSON-tmLanguage +94 -94
  94. data/subl/Ace (Ruby).tmLanguage +153 -153
  95. metadata +17 -6
  96. data/lib/antelope/generator/templates/output.ant +0 -68
@@ -1,264 +1,264 @@
1
- require 'hashie/mash'
2
-
3
- module Antelope
4
- module Generator
5
-
6
- # Generates a parser. This is normally the parent class, and the
7
- # specific implementations inherit from this. The generated
8
- # parser should, ideally, be completely independent (not requiring
9
- # any external source code), as well as be under a permissive
10
- # license.
11
- #
12
- # @abstract Subclass and redefine {#generate} to create a
13
- # generator.
14
- class Base
15
- Boolean = Object.new
16
- # The modifiers that were applied to the grammar.
17
- #
18
- # @return [Hash<(Symbol, Object)>]
19
- attr_reader :mods
20
-
21
- # The file name (not including the extension) that the grammar
22
- # should output to.
23
- #
24
- # @return [String]
25
- attr_reader :file
26
-
27
- # The grammar that the generator is for.
28
- #
29
- # @return [Ace::Grammar]
30
- attr_reader :grammar
31
-
32
- # The source root directory for templates. Overwrite to change.
33
- #
34
- # @return [Pathname]
35
- def self.source_root
36
- Pathname.new("../templates").expand_path(__FILE__)
37
- end
38
-
39
- def self.register_as(*names)
40
- Generator.register_generator(self, *names)
41
- end
42
-
43
- # Called by ruby on subclassing.
44
- #
45
- # @param subclass [Class]
46
- # @return [void]
47
- def self.inherited(subclass)
48
- directives.each do |name, (_, type)|
49
- subclass.has_directive(name, type)
50
- end
51
- end
52
-
53
- # Allows a directive for this generator. This is checked in
54
- # the compiler to allow the option. If the compiler encounters
55
- # a bad directive, it'll error (to give the developer a warning).
56
- #
57
- # @param directive [Symbol, String]
58
- # @param type [Object] used to define how the value should be
59
- # coerced.
60
- # @see #directives
61
- # @see #coerce_directive_value
62
- # @return [void]
63
- def self.has_directive(directive, type = nil)
64
- directive = directive.to_s
65
- directives[directive] = [self, type]
66
- end
67
-
68
- # The directives in the class.
69
- #
70
- # @see .has_directive
71
- # @return [Hash]
72
- def self.directives
73
- @_directives ||= {}
74
- end
75
-
76
- class << self
77
- alias_method :has_directives, :has_directive
78
- end
79
-
80
- # Initialize the generator.
81
- #
82
- # @param grammar [Grammar]
83
- # @param mods [Hash<(Symbol, Object)>]
84
- def initialize(grammar, mods)
85
- @file = grammar.name
86
- @grammar = grammar
87
- @mods = mods
88
- end
89
-
90
- # Actually does the generation. A subclass should implement this.
91
- #
92
- # @raise [NotImplementedError]
93
- # @return [void]
94
- def generate
95
- raise NotImplementedError
96
- end
97
-
98
- protected
99
-
100
- # Retrieves all directives from the grammar, and giving them the
101
- # proper values for this instance.
102
- #
103
- # @see .has_directive
104
- # @see #coerce_directive_value
105
- # @return [Hash]
106
- def directives
107
- @_directives ||= begin
108
- hash = Hashie::Mash.new
109
-
110
- self.class.directives.each do |key, dict|
111
- value = [grammar.options.key?(key), grammar.options[key]]
112
- hash.deep_merge! coerce_nested_hash(key,
113
- coerce_directive_value(*value, dict[1]))
114
- end
115
-
116
- hash
117
- end
118
- end
119
-
120
- def coerce_nested_hash(key, value)
121
- parts = key.split('.').map { |p| p.gsub(/-/, "_") }
122
- top = {}
123
- hash = top
124
- parts.each_with_index do |part, i|
125
- hash[part] = if parts.last == part
126
- value
127
- else
128
- {}
129
- end
130
- hash = hash[part]
131
- end
132
-
133
- top[key] = value
134
- top
135
- end
136
-
137
- # Coerce the given directive value to the given type. For the
138
- # type `nil`, it checks the size of the values; for no values,
139
- # it returns true; for one value, it returns that one value; for
140
- # any other size value, it returns the values. For the type
141
- # `Boolean`, if no values were given, or if the first value isn't
142
- # "false", it returns true. For the type `:single` (or `:one`),
143
- # it returns the first value. For the type `Array`, it returns
144
- # the values. For any other type that is a class, it tries to
145
- # initialize the class with the given arguments.
146
- def coerce_directive_value(defined, values, type)
147
- return nil unless defined || Array === type
148
- case type
149
- when nil
150
- case values.size
151
- when 0
152
- true
153
- when 1
154
- values[0]
155
- else
156
- values
157
- end
158
- when :single, :one
159
- values[0]
160
- when Boolean
161
- # For bool, if there were no arguments, then return true;
162
- # otherwise, if the first argument isn't "false", return
163
- # true.
164
-
165
- values[0].to_s != "false"
166
- when Array
167
- values.zip(type).map do |value, t|
168
- coerce_directive_value(defined, [value], t)
169
- end
170
- when Class
171
- if type == Array
172
- values
173
- elsif type == String
174
- values[0].to_s
175
- elsif [Fixnum, Integer, Numeric].include?(type)
176
- values[0].to_i
177
- elsif type == Float
178
- values[0].to_f
179
- else
180
- type.new(*values)
181
- end
182
- else
183
- raise UnknownTypeError, "unknown type #{type}"
184
- end
185
- end
186
-
187
- # Copies a template from the source, runs it through erb (in the
188
- # context of this class), and then outputs it at the destination.
189
- # If given a block, it will call the block after the template is
190
- # run through erb with the content from erb; the result of the
191
- # block is then used as the content instead.
192
- #
193
- # @param source [String] the source file. This should be in
194
- # {.source_root}.
195
- # @param destination [String] the destination file. This will be
196
- # in {Ace::Grammar#output}.
197
- # @yieldparam [String] content The content that ERB created.
198
- # @yieldreturn [String] The new content to write to the output.
199
- # @return [void]
200
- def template(source, destination)
201
- src = Pathname.new("#{source}.ant").
202
- expand_path(self.class.source_root)
203
-
204
- template = Template.new(src)
205
- content = template.result(instance_eval('binding'))
206
- content.gsub!(/[ \t]+\n/, "\n")
207
-
208
- if block_given?
209
- content = yield content
210
- end
211
-
212
- dest = Pathname.new(destination).
213
- expand_path(grammar.output)
214
-
215
- dest.open("w") do |file|
216
- file.write(content)
217
- end
218
- end
219
-
220
- # The actual table that is used for parsing. This returns an
221
- # array of hashes; the array index corresponds to the state
222
- # number, and the hash keys correspond to the lookahead tokens.
223
- # The hash values are an array; the first element of that array
224
- # is the action to be taken, and the second element of the
225
- # array is the argument for that action. Possible actions
226
- # include `:accept`, `:reduce`, and `:state`; `:accept` means
227
- # to accept the string; `:reduce` means to perform the given
228
- # reduction; and `:state` means to transition to the given
229
- # state.
230
- #
231
- # @return [Array<Hash<Symbol => Array<(Symbol, Numeric)>>>]
232
- def table
233
- if mods[:tableizer].is_a? Generation::Tableizer
234
- mods[:tableizer].table
235
- else
236
- []
237
- end
238
- end
239
-
240
- # Returns an array of the production information of each
241
- # production needed by the parser. The first element of any
242
- # element in the array is an {Ace::Token::Nonterminal} that
243
- # that specific production reduces to; the second element
244
- # is a number describing the number of items in the right hand
245
- # side of the production; the string represents the action
246
- # that should be taken on reduction.
247
- #
248
- # This information is used for `:reduce` actions in the parser;
249
- # the value of the `:reduce` action corresponds to the array
250
- # index of the production in this array.
251
- #
252
- # @return [Array<Array<(Ace::Token::Nonterminal, Numeric, String)>]
253
- def productions
254
- grammar.all_productions.map do |production|
255
- [production[:label],
256
- production[:items].size,
257
- production[:block]]
258
- end
259
- end
260
-
261
-
262
- end
263
- end
264
- end
1
+ require 'hashie/mash'
2
+
3
+ module Antelope
4
+ module Generator
5
+
6
+ # Generates a parser. This is normally the parent class, and the
7
+ # specific implementations inherit from this. The generated
8
+ # parser should, ideally, be completely independent (not requiring
9
+ # any external source code), as well as be under a permissive
10
+ # license.
11
+ #
12
+ # @abstract Subclass and redefine {#generate} to create a
13
+ # generator.
14
+ class Base
15
+ Boolean = Object.new
16
+ # The modifiers that were applied to the grammar.
17
+ #
18
+ # @return [Hash<(Symbol, Object)>]
19
+ attr_reader :mods
20
+
21
+ # The file name (not including the extension) that the grammar
22
+ # should output to.
23
+ #
24
+ # @return [String]
25
+ attr_reader :file
26
+
27
+ # The grammar that the generator is for.
28
+ #
29
+ # @return [Ace::Grammar]
30
+ attr_reader :grammar
31
+
32
+ # The source root directory for templates. Overwrite to change.
33
+ #
34
+ # @return [Pathname]
35
+ def self.source_root
36
+ Pathname.new("../templates").expand_path(__FILE__)
37
+ end
38
+
39
+ def self.register_as(*names)
40
+ Generator.register_generator(self, *names)
41
+ end
42
+
43
+ # Called by ruby on subclassing.
44
+ #
45
+ # @param subclass [Class]
46
+ # @return [void]
47
+ def self.inherited(subclass)
48
+ directives.each do |name, (_, type)|
49
+ subclass.has_directive(name, type)
50
+ end
51
+ end
52
+
53
+ # Allows a directive for this generator. This is checked in
54
+ # the compiler to allow the option. If the compiler encounters
55
+ # a bad directive, it'll error (to give the developer a warning).
56
+ #
57
+ # @param directive [Symbol, String]
58
+ # @param type [Object] used to define how the value should be
59
+ # coerced.
60
+ # @see #directives
61
+ # @see #coerce_directive_value
62
+ # @return [void]
63
+ def self.has_directive(directive, type = nil)
64
+ directive = directive.to_s
65
+ directives[directive] = [self, type]
66
+ end
67
+
68
+ # The directives in the class.
69
+ #
70
+ # @see .has_directive
71
+ # @return [Hash]
72
+ def self.directives
73
+ @_directives ||= {}
74
+ end
75
+
76
+ class << self
77
+ alias_method :has_directives, :has_directive
78
+ end
79
+
80
+ # Initialize the generator.
81
+ #
82
+ # @param grammar [Grammar]
83
+ # @param mods [Hash<(Symbol, Object)>]
84
+ def initialize(grammar, mods)
85
+ @file = grammar.name
86
+ @grammar = grammar
87
+ @mods = mods
88
+ end
89
+
90
+ # Actually does the generation. A subclass should implement this.
91
+ #
92
+ # @raise [NotImplementedError]
93
+ # @return [void]
94
+ def generate
95
+ raise NotImplementedError
96
+ end
97
+
98
+ protected
99
+
100
+ # Retrieves all directives from the grammar, and giving them the
101
+ # proper values for this instance.
102
+ #
103
+ # @see .has_directive
104
+ # @see #coerce_directive_value
105
+ # @return [Hash]
106
+ def directives
107
+ @_directives ||= begin
108
+ hash = Hashie::Mash.new
109
+
110
+ self.class.directives.each do |key, dict|
111
+ value = [grammar.options.key?(key), grammar.options[key]]
112
+ hash.deep_merge! coerce_nested_hash(key,
113
+ coerce_directive_value(*value, dict[1]))
114
+ end
115
+
116
+ hash
117
+ end
118
+ end
119
+
120
+ def coerce_nested_hash(key, value)
121
+ parts = key.split('.').map { |p| p.gsub(/-/, "_") }
122
+ top = {}
123
+ hash = top
124
+ parts.each_with_index do |part, i|
125
+ hash[part] = if parts.last == part
126
+ value
127
+ else
128
+ {}
129
+ end
130
+ hash = hash[part]
131
+ end
132
+
133
+ top[key] = value
134
+ top
135
+ end
136
+
137
+ # Coerce the given directive value to the given type. For the
138
+ # type `nil`, it checks the size of the values; for no values,
139
+ # it returns true; for one value, it returns that one value; for
140
+ # any other size value, it returns the values. For the type
141
+ # `Boolean`, if no values were given, or if the first value isn't
142
+ # "false", it returns true. For the type `:single` (or `:one`),
143
+ # it returns the first value. For the type `Array`, it returns
144
+ # the values. For any other type that is a class, it tries to
145
+ # initialize the class with the given arguments.
146
+ def coerce_directive_value(defined, values, type)
147
+ return nil unless defined || Array === type
148
+ case type
149
+ when nil
150
+ case values.size
151
+ when 0
152
+ true
153
+ when 1
154
+ values[0]
155
+ else
156
+ values
157
+ end
158
+ when :single, :one
159
+ values[0]
160
+ when Boolean
161
+ # For bool, if there were no arguments, then return true;
162
+ # otherwise, if the first argument isn't "false", return
163
+ # true.
164
+
165
+ values[0].to_s != "false"
166
+ when Array
167
+ values.zip(type).map do |value, t|
168
+ coerce_directive_value(defined, [value], t)
169
+ end
170
+ when Class
171
+ if type == Array
172
+ values
173
+ elsif type == String
174
+ values[0].to_s
175
+ elsif [Fixnum, Integer, Numeric].include?(type)
176
+ values[0].to_i
177
+ elsif type == Float
178
+ values[0].to_f
179
+ else
180
+ type.new(*values)
181
+ end
182
+ else
183
+ raise UnknownTypeError, "unknown type #{type}"
184
+ end
185
+ end
186
+
187
+ # Copies a template from the source, runs it through erb (in the
188
+ # context of this class), and then outputs it at the destination.
189
+ # If given a block, it will call the block after the template is
190
+ # run through erb with the content from erb; the result of the
191
+ # block is then used as the content instead.
192
+ #
193
+ # @param source [String] the source file. This should be in
194
+ # {.source_root}.
195
+ # @param destination [String] the destination file. This will be
196
+ # in {Ace::Grammar#output}.
197
+ # @yieldparam [String] content The content that ERB created.
198
+ # @yieldreturn [String] The new content to write to the output.
199
+ # @return [void]
200
+ def template(source, destination)
201
+ src = Pathname.new("#{source}.ant").
202
+ expand_path(self.class.source_root)
203
+
204
+ template = Template.new(src)
205
+ content = template.result(instance_eval('binding'))
206
+ content.gsub!(/[ \t]+\n/, "\n")
207
+
208
+ if block_given?
209
+ content = yield content
210
+ end
211
+
212
+ dest = Pathname.new(destination).
213
+ expand_path(grammar.output)
214
+
215
+ dest.open("w") do |file|
216
+ file.write(content)
217
+ end
218
+ end
219
+
220
+ # The actual table that is used for parsing. This returns an
221
+ # array of hashes; the array index corresponds to the state
222
+ # number, and the hash keys correspond to the lookahead tokens.
223
+ # The hash values are an array; the first element of that array
224
+ # is the action to be taken, and the second element of the
225
+ # array is the argument for that action. Possible actions
226
+ # include `:accept`, `:reduce`, and `:state`; `:accept` means
227
+ # to accept the string; `:reduce` means to perform the given
228
+ # reduction; and `:state` means to transition to the given
229
+ # state.
230
+ #
231
+ # @return [Array<Hash<Symbol => Array<(Symbol, Numeric)>>>]
232
+ def table
233
+ if mods[:tableizer].is_a? Generation::Tableizer
234
+ mods[:tableizer].table
235
+ else
236
+ []
237
+ end
238
+ end
239
+
240
+ # Returns an array of the production information of each
241
+ # production needed by the parser. The first element of any
242
+ # element in the array is an {Ace::Token::Nonterminal} that
243
+ # that specific production reduces to; the second element
244
+ # is a number describing the number of items in the right hand
245
+ # side of the production; the string represents the action
246
+ # that should be taken on reduction.
247
+ #
248
+ # This information is used for `:reduce` actions in the parser;
249
+ # the value of the `:reduce` action corresponds to the array
250
+ # index of the production in this array.
251
+ #
252
+ # @return [Array<Array<(Ace::Token::Nonterminal, Numeric, String)>]
253
+ def productions
254
+ grammar.all_productions.map do |production|
255
+ [production[:label],
256
+ production[:items].size,
257
+ production[:block]]
258
+ end
259
+ end
260
+
261
+
262
+ end
263
+ end
264
+ end
@@ -1,11 +1,11 @@
1
- module Antelope
2
- module Generator
3
- class C < Group
4
- register_as "c", "C"
5
-
6
- register_generator CHeader, "c-header"
7
- register_generator CSource, "c-source"
8
-
9
- end
10
- end
11
- end
1
+ module Antelope
2
+ module Generator
3
+ class C < Group
4
+ register_as "c", "C"
5
+
6
+ register_generator CHeader, "c-header"
7
+ register_generator CSource, "c-source"
8
+
9
+ end
10
+ end
11
+ end