antelope 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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