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,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