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,264 +1,134 @@
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, _|
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 'antelope/generator/base/coerce'
2
+ require 'antelope/generator/base/extra'
3
+ require 'hashie/mash'
4
+
5
+ module Antelope
6
+ module Generator
7
+ # Generates a parser. This is normally the parent class, and the
8
+ # specific implementations inherit from this. The generated
9
+ # parser should, ideally, be completely independent (not requiring
10
+ # any external source code), as well as be under a permissive
11
+ # license.
12
+ #
13
+ # @abstract Subclass and redefine {#generate} to create a
14
+ # generator.
15
+ class Base
16
+ include Coerce
17
+ include Extra
18
+
19
+ Boolean = Object.new
20
+ # The modifiers that were applied to the grammar.
21
+ #
22
+ # @return [Hash<(Symbol, Object)>]
23
+ attr_reader :mods
24
+
25
+ # The file name (not including the extension) that the grammar
26
+ # should output to.
27
+ #
28
+ # @return [String]
29
+ attr_reader :file
30
+
31
+ # The grammar that the generator is for.
32
+ #
33
+ # @return [Ace::Grammar]
34
+ attr_reader :grammar
35
+
36
+ # The source root directory for templates. Overwrite to change.
37
+ #
38
+ # @return [Pathname]
39
+ def self.source_root
40
+ Pathname.new('../templates').expand_path(__FILE__)
41
+ end
42
+
43
+ def self.register_as(*names)
44
+ Generator.register_generator(self, *names)
45
+ end
46
+
47
+ # Called by ruby on subclassing.
48
+ #
49
+ # @param subclass [Class]
50
+ # @return [void]
51
+ def self.inherited(subclass)
52
+ directives.each do |name, (_, type)|
53
+ subclass.directive(name, type)
54
+ end
55
+ end
56
+
57
+ # Allows a directive for this generator. This is checked in
58
+ # the compiler to allow the option. If the compiler encounters
59
+ # a bad directive, it'll error (to give the developer a warning).
60
+ #
61
+ # @param directive [Symbol, String]
62
+ # @param type [Object] used to define how the value should be
63
+ # coerced.
64
+ # @see #directives
65
+ # @see #coerce_directive_value
66
+ # @return [void]
67
+ def self.directive(directive, type = nil)
68
+ directive = directive.to_s
69
+ directives[directive] = [self, type]
70
+ end
71
+
72
+ # The directives in the class.
73
+ #
74
+ # @see .has_directive
75
+ # @return [Hash]
76
+ def self.directives
77
+ @_directives ||= {}
78
+ end
79
+
80
+ class << self
81
+ alias_method :has_directives, :directive
82
+ alias_method :has_directive, :directive
83
+ end
84
+
85
+ # Initialize the generator.
86
+ #
87
+ # @param grammar [Grammar]
88
+ # @param mods [Hash<(Symbol, Object)>]
89
+ def initialize(grammar, mods)
90
+ @file = grammar.name
91
+ @grammar = grammar
92
+ @mods = mods
93
+ end
94
+
95
+ # Actually does the generation. A subclass should implement
96
+ # this.
97
+ #
98
+ # @raise [NotImplementedError]
99
+ # @return [void]
100
+ def generate
101
+ raise NotImplementedError
102
+ end
103
+
104
+ # Copies a template from the source, runs it through mote (in the
105
+ # context of this class), and then outputs it at the destination.
106
+ # If given a block, it will call the block after the template is
107
+ # run through erb with the content from erb; the result of the
108
+ # block is then used as the content instead.
109
+ #
110
+ # @param source [String] the source file. This should be in
111
+ # {.source_root}.
112
+ # @param destination [String] the destination file. This will be
113
+ # in {Ace::Grammar#output}.
114
+ # @yieldparam [String] content The content that ERB created.
115
+ # @yieldreturn [String] The new content to write to the output.
116
+ # @return [void]
117
+ def template(source, destination)
118
+ src = Pathname.new("#{source}.erb")
119
+ .expand_path(self.class.source_root)
120
+
121
+ template = ERB.new(src.read, nil, '-')
122
+ content = template.result(instance_eval('binding'))
123
+
124
+ block_given? && content = yield(content)
125
+
126
+ dest = Pathname.new(destination).expand_path(grammar.output)
127
+
128
+ dest.open('w') do |file|
129
+ file.write(content)
130
+ end
131
+ end
132
+ end
133
+ end
134
+ 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
@@ -1,105 +1,105 @@
1
- module Antelope
2
- module Generator
3
- class CHeader < Base
4
-
5
- register_as "c-header", "c_header"
6
-
7
- has_directive "union", Array[String, String]
8
- has_directive "api.prefix", String
9
- has_directive "api.push-pull", String
10
- has_directive "api.value.type", String
11
- has_directive "api.token.prefix", String
12
- has_directive "parse-param", Array
13
- has_directive "lex-param", Array
14
- has_directive "param", Array
15
-
16
- def push?
17
- directives.api.push_pull == "push"
18
- end
19
-
20
- def define_stype?
21
- !!directives.union[0] && !directives.api.value.type
22
- end
23
-
24
- def lex_params
25
- params = [directives.lex_param, directives.param].compact.
26
- flatten
27
-
28
- if params.any?
29
- ", " << params.join(", ")
30
- else
31
- ""
32
- end
33
- end
34
-
35
- def parse_params
36
- [directives.parse_param, directives.param].compact.flatten.
37
- join(", ")
38
- end
39
-
40
- def params
41
- if directives.param
42
- directives.param.join(", ")
43
- else
44
- ""
45
- end
46
- end
47
-
48
- def stype
49
- prefix.upcase << if directives.api.value.type
50
- directives.api.value.type
51
- elsif directives.union.size > 1
52
- directives.union[0]
53
- else
54
- "STYPE"
55
- end
56
- end
57
-
58
- def union_body
59
- directives.union.last
60
- end
61
-
62
- def terminal_type
63
- "int" # for now
64
- end
65
-
66
- def token_prefix
67
- if directives.api.token.prefix
68
- directives.api.token.prefix
69
- elsif directives.api.prefix
70
- prefix.upcase
71
- else
72
- ""
73
- end
74
- end
75
-
76
- def prefix
77
- if directives.api.prefix
78
- directives.api.prefix
79
- else
80
- "yy"
81
- end
82
- end
83
-
84
- def upper_prefix
85
- prefix.upcase
86
- end
87
-
88
- def symbols
89
- @_symbols ||= begin
90
- sym = grammar.terminals.map(&:name) + grammar.nonterminals
91
- nums = sym.each_with_index.map { |v, i| [v, i + 257] }
92
- Hash[nums]
93
- end
94
- end
95
-
96
- def guard_name
97
- "#{prefix.upcase}#{file.gsub(/[\W]/, "_").upcase}"
98
- end
99
-
100
- def generate
101
- template "c_header", "#{file}.h"
102
- end
103
- end
104
- end
105
- end
1
+ module Antelope
2
+ module Generator
3
+ class CHeader < Base
4
+
5
+ register_as "c-header", "c_header"
6
+
7
+ has_directive "union", Array[String, String]
8
+ has_directive "api.prefix", String
9
+ has_directive "api.push-pull", String
10
+ has_directive "api.value.type", String
11
+ has_directive "api.token.prefix", String
12
+ has_directive "parse-param", Array
13
+ has_directive "lex-param", Array
14
+ has_directive "param", Array
15
+
16
+ def push?
17
+ directives.api.push_pull == "push"
18
+ end
19
+
20
+ def define_stype?
21
+ !!directives.union[0] && !directives.api.value.type
22
+ end
23
+
24
+ def lex_params
25
+ params = [directives.lex_param, directives.param].compact.
26
+ flatten
27
+
28
+ if params.any?
29
+ ", " << params.join(", ")
30
+ else
31
+ ""
32
+ end
33
+ end
34
+
35
+ def parse_params
36
+ [directives.parse_param, directives.param].compact.flatten.
37
+ join(", ")
38
+ end
39
+
40
+ def params
41
+ if directives.param
42
+ directives.param.join(", ")
43
+ else
44
+ ""
45
+ end
46
+ end
47
+
48
+ def stype
49
+ prefix.upcase << if directives.api.value.type
50
+ directives.api.value.type
51
+ elsif directives.union.size > 1
52
+ directives.union[0]
53
+ else
54
+ "STYPE"
55
+ end
56
+ end
57
+
58
+ def union_body
59
+ directives.union.last
60
+ end
61
+
62
+ def terminal_type
63
+ "int" # for now
64
+ end
65
+
66
+ def token_prefix
67
+ if directives.api.token.prefix
68
+ directives.api.token.prefix
69
+ elsif directives.api.prefix
70
+ prefix.upcase
71
+ else
72
+ ""
73
+ end
74
+ end
75
+
76
+ def prefix
77
+ if directives.api.prefix
78
+ directives.api.prefix
79
+ else
80
+ "yy"
81
+ end
82
+ end
83
+
84
+ def upper_prefix
85
+ prefix.upcase
86
+ end
87
+
88
+ def symbols
89
+ @_symbols ||= begin
90
+ sym = grammar.terminals.map(&:name) + grammar.nonterminals
91
+ nums = sym.each_with_index.map { |v, i| [v, i + 257] }
92
+ Hash[nums]
93
+ end
94
+ end
95
+
96
+ def guard_name
97
+ "#{prefix.upcase}#{file.gsub(/[\W]/, "_").upcase}"
98
+ end
99
+
100
+ def generate
101
+ template "c_header", "#{file}.h"
102
+ end
103
+ end
104
+ end
105
+ end