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,176 +1,176 @@
1
- # encoding: utf-8
2
-
3
- module Antelope
4
- module Generation
5
-
6
- # Constructs the table required for the parser.
7
- class Tableizer
8
-
9
- # The grammar that the table is based off of.
10
- #
11
- # @return [Grammar]
12
- attr_accessor :grammar
13
-
14
- # The table itself.
15
- #
16
- # @return [Array<Hash<(Symbol, Array<(Symbol, Numeric)>)>>]
17
- attr_accessor :table
18
-
19
- # All rules in the grammar.
20
- #
21
- # @return [Hash<(Numeric, Recognizer::Rule)>]
22
- attr_accessor :rules
23
-
24
- attr_reader :conflicts
25
-
26
- # Initialize.
27
- #
28
- # @param grammar [Grammar]
29
- def initialize(grammar)
30
- @grammar = grammar
31
- end
32
-
33
- # Construct the table, and then check the table for conflicts.
34
- #
35
- # @return [void]
36
- # @see #tablize
37
- # @see #conflictize
38
- def call
39
- tablize
40
- conflictize
41
- defaultize
42
- end
43
-
44
- # Construct a table based on the grammar. The table itself is
45
- # an array whose elements are hashes; the index of the array
46
- # corresponds to the state ID, and the keys of the hashes
47
- # correspond to acceptable tokens. The values of the hashes
48
- # should be an array of arrays (at this point).
49
- #
50
- # @return [void]
51
- def tablize
52
- @table = Array.new(grammar.states.size) do
53
- Hash.new { |h, k| h[k] = [] }
54
- end
55
- @rules = []
56
-
57
- grammar.states.each do |state|
58
- state.transitions.each do |on, to|
59
- table[state.id][on] << [:state, to.id]
60
- end
61
-
62
- state.rules.each do |rule|
63
- @rules[rule.production.id] = rule.production
64
- if rule.final?
65
- rule.lookahead.each do |look|
66
- table[state.id][look.name] <<
67
- [:reduce, rule.production.id]
68
- end
69
-
70
- if rule.production.id.zero?
71
- table[state.id][:$end] =
72
- [[:accept, rule.production.id]]
73
- end
74
- end
75
- end
76
- end
77
-
78
- table
79
- end
80
-
81
- # Resolve any conflicts through precedence, if we can. If we
82
- # can't, let the user know. This makes sure that every value
83
- # of the hashes is a single array.
84
- #
85
- # @raise [UnresolvableConflictError] if a conflict could not be
86
- # resolved using precedence rules.
87
- # @return [void]
88
- def conflictize
89
- states = grammar.states.to_a.sort_by(&:id)
90
- @conflicts = Hash.new { |h, k| h[k] = {} }
91
- @table.each_with_index do |v, state|
92
- v.each do |on, data|
93
- if data.size == 1
94
- @table[state][on] = data[0]
95
- next
96
- end
97
-
98
- terminal = if states[state].transitions.key?(on)
99
- states[state].rules.
100
- detect { |rule| rule.active.name == on }.precedence
101
- end
102
- rule_part, other_part = data.sort_by { |(t, _)| t }
103
-
104
- conflict = proc do |result|
105
- hash = { result: result,
106
- terminal: terminal,
107
- prec: @rules[rule_part[1]].prec,
108
- data: data,
109
- rules: [], transitions: [] }
110
-
111
- hash[:rules].concat(data.select { |part|
112
- part[0] == :reduce || part[0] == :accept
113
- }.map { |(_, id)|
114
- states[state].rules.select(&:final?).
115
- detect { |rule| rule.production.id == id }
116
- })
117
- hash[:transitions].concat(data.select { |part|
118
- part[0] == :state
119
- }.map { |_|
120
- states[state].rules.
121
- detect { |rule| rule.active.name == on }
122
- })
123
-
124
- conflicts[state][on] = hash
125
- end
126
-
127
- unless other_part[0] == :state
128
- conflict.call(0)
129
- $stderr.puts \
130
- "Could not determine move for #{on} in state " \
131
- "#{state} (reduce/reduce conflict)"
132
- next
133
- end
134
-
135
- result = @rules[rule_part[1]].prec <=> terminal
136
- conflict.call(result)
137
-
138
- case result
139
- when 0
140
- @table[state][on] = nil
141
- $stderr.puts \
142
- "Could not determine move for #{on} in state " \
143
- "#{state} (shift/reduce conflict)"
144
- when 1
145
- @table[state][on] = rule_part
146
- when -1
147
- @table[state][on] = other_part
148
- end
149
- end
150
- end
151
- end
152
-
153
- # Reduce many transitions into a single `$default` transition.
154
- # This only works if there is no `$empty` transition; if there
155
- # is an `$empty` transition, then the `$default` transition is
156
- # set to be the `$empty` transition.
157
- #
158
- # @return [void]
159
- def defaultize
160
- max = @table.map { |s| s.keys.size }.max
161
- @table.each_with_index do |state|
162
- common = state.group_by { |k, v| v }.values.
163
- sort_by(&:size).first
164
-
165
- if common.size > (max / 2)
166
- action = common[0][1]
167
-
168
- keys = common.map(&:first)
169
- state.delete_if { |k, _| keys.include?(k) }
170
- state[:$default] = action
171
- end
172
- end
173
- end
174
- end
175
- end
176
- end
1
+ # encoding: utf-8
2
+
3
+ module Antelope
4
+ module Generation
5
+
6
+ # Constructs the table required for the parser.
7
+ class Tableizer
8
+
9
+ # The grammar that the table is based off of.
10
+ #
11
+ # @return [Grammar]
12
+ attr_accessor :grammar
13
+
14
+ # The table itself.
15
+ #
16
+ # @return [Array<Hash<(Symbol, Array<(Symbol, Numeric)>)>>]
17
+ attr_accessor :table
18
+
19
+ # All rules in the grammar.
20
+ #
21
+ # @return [Hash<(Numeric, Recognizer::Rule)>]
22
+ attr_accessor :rules
23
+
24
+ attr_reader :conflicts
25
+
26
+ # Initialize.
27
+ #
28
+ # @param grammar [Grammar]
29
+ def initialize(grammar)
30
+ @grammar = grammar
31
+ end
32
+
33
+ # Construct the table, and then check the table for conflicts.
34
+ #
35
+ # @return [void]
36
+ # @see #tablize
37
+ # @see #conflictize
38
+ def call
39
+ tablize
40
+ conflictize
41
+ defaultize
42
+ end
43
+
44
+ # Construct a table based on the grammar. The table itself is
45
+ # an array whose elements are hashes; the index of the array
46
+ # corresponds to the state ID, and the keys of the hashes
47
+ # correspond to acceptable tokens. The values of the hashes
48
+ # should be an array of arrays (at this point).
49
+ #
50
+ # @return [void]
51
+ def tablize
52
+ @table = Array.new(grammar.states.size) do
53
+ Hash.new { |h, k| h[k] = [] }
54
+ end
55
+ @rules = []
56
+
57
+ grammar.states.each do |state|
58
+ state.transitions.each do |on, to|
59
+ table[state.id][on] << [:state, to.id]
60
+ end
61
+
62
+ state.rules.each do |rule|
63
+ @rules[rule.production.id] = rule.production
64
+ if rule.final?
65
+ rule.lookahead.each do |look|
66
+ table[state.id][look.name] <<
67
+ [:reduce, rule.production.id]
68
+ end
69
+
70
+ if rule.production.id.zero?
71
+ table[state.id][:$end] =
72
+ [[:accept, rule.production.id]]
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ table
79
+ end
80
+
81
+ # Resolve any conflicts through precedence, if we can. If we
82
+ # can't, let the user know. This makes sure that every value
83
+ # of the hashes is a single array.
84
+ #
85
+ # @raise [UnresolvableConflictError] if a conflict could not be
86
+ # resolved using precedence rules.
87
+ # @return [void]
88
+ def conflictize
89
+ states = grammar.states.to_a.sort_by(&:id)
90
+ @conflicts = Hash.new { |h, k| h[k] = {} }
91
+ @table.each_with_index do |v, state|
92
+ v.each do |on, data|
93
+ if data.size == 1
94
+ @table[state][on] = data[0]
95
+ next
96
+ end
97
+
98
+ terminal = if states[state].transitions.key?(on)
99
+ states[state].rules.
100
+ detect { |rule| rule.active.name == on }.precedence
101
+ end
102
+ rule_part, other_part = data.sort_by { |(t, _)| t }
103
+
104
+ conflict = proc do |result|
105
+ hash = { result: result,
106
+ terminal: terminal,
107
+ prec: @rules[rule_part[1]].prec,
108
+ data: data,
109
+ rules: [], transitions: [] }
110
+
111
+ hash[:rules].concat(data.select { |part|
112
+ part[0] == :reduce || part[0] == :accept
113
+ }.map { |(_, id)|
114
+ states[state].rules.select(&:final?).
115
+ detect { |rule| rule.production.id == id }
116
+ })
117
+ hash[:transitions].concat(data.select { |part|
118
+ part[0] == :state
119
+ }.map { |_|
120
+ states[state].rules.
121
+ detect { |rule| rule.active.name == on }
122
+ })
123
+
124
+ conflicts[state][on] = hash
125
+ end
126
+
127
+ unless other_part[0] == :state
128
+ conflict.call(0)
129
+ $stderr.puts \
130
+ "Could not determine move for #{on} in state " \
131
+ "#{state} (reduce/reduce conflict)"
132
+ next
133
+ end
134
+
135
+ result = @rules[rule_part[1]].prec <=> terminal
136
+ conflict.call(result)
137
+
138
+ case result
139
+ when 0
140
+ @table[state][on] = nil
141
+ $stderr.puts \
142
+ "Could not determine move for #{on} in state " \
143
+ "#{state} (shift/reduce conflict)"
144
+ when 1
145
+ @table[state][on] = rule_part
146
+ when -1
147
+ @table[state][on] = other_part
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ # Reduce many transitions into a single `$default` transition.
154
+ # This only works if there is no `$empty` transition; if there
155
+ # is an `$empty` transition, then the `$default` transition is
156
+ # set to be the `$empty` transition.
157
+ #
158
+ # @return [void]
159
+ def defaultize
160
+ max = @table.map { |s| s.keys.size }.max
161
+ @table.each_with_index do |state|
162
+ common = state.group_by { |k, v| v }.values.
163
+ sort_by(&:size).first
164
+
165
+ if common.size > (max / 2)
166
+ action = common[0][1]
167
+
168
+ keys = common.map(&:first)
169
+ state.delete_if { |k, _| keys.include?(k) }
170
+ state[:$default] = action
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -1,15 +1,15 @@
1
- # encoding: utf-8
2
-
3
- require "antelope/generation/errors"
4
- require "antelope/generation/constructor"
5
- require "antelope/generation/recognizer"
6
- require "antelope/generation/tableizer"
7
- require "antelope/generation/null"
8
-
9
- module Antelope
10
-
11
- # Contains the generation mods.
12
- module Generation
13
-
14
- end
15
- end
1
+ # encoding: utf-8
2
+
3
+ require "antelope/generation/errors"
4
+ require "antelope/generation/constructor"
5
+ require "antelope/generation/recognizer"
6
+ require "antelope/generation/tableizer"
7
+ require "antelope/generation/null"
8
+
9
+ module Antelope
10
+
11
+ # Contains the generation mods.
12
+ module Generation
13
+
14
+ end
15
+ end
@@ -0,0 +1,115 @@
1
+ module Antelope
2
+ module Generator
3
+ class Base
4
+ # Handles coercion of directives and their values.
5
+ module Coerce
6
+ # Retrieves all directives from the grammar, and giving them
7
+ # the proper values for this instance.
8
+ #
9
+ # @see .directive?
10
+ # @see #coerce_directive_value
11
+ # @return [Hash]
12
+ def directives
13
+ @_directives ||= begin
14
+ hash = Hashie::Mash.new
15
+
16
+ self.class.directives.each do |key, (_, definition)|
17
+ directive_value =
18
+ coerce_directive_value(grammar.options.key?(key),
19
+ grammar.options[key], definition)
20
+ value = coerce_nested_hash(key, directive_value)
21
+ hash.deep_merge!(value)
22
+ end
23
+
24
+ hash
25
+ end
26
+ end
27
+
28
+ # Coerces a key of the format `<name>[.<name>]*` into a full
29
+ # hash accessable by ruby.
30
+ #
31
+ # @param key [String] the key of the directive.
32
+ # @param value [String] the value of the directive.
33
+ # @return [Hash] the resultant hash.
34
+ def coerce_nested_hash(key, value)
35
+ parts = key.split('.').map { |p| p.gsub(/-/, '_') }
36
+ top = {}
37
+ hash = top
38
+ parts.each do |part|
39
+ hash[part] = if parts.last == part
40
+ value
41
+ else
42
+ {}
43
+ end
44
+ hash = hash[part]
45
+ end
46
+
47
+ top[key] = value
48
+ top
49
+ end
50
+
51
+ # Coerce the given directive value to the given type. For the
52
+ # type `nil`, it checks the size of the values; for no values,
53
+ # it returns true; for one value, it returns that one value;
54
+ # for any other size value, it returns the values. For the
55
+ # type `Boolean`, if no values were given, or if the first
56
+ # value isn't "false", it returns true. For the type
57
+ # `:single` (or `:one`), it returns the first value. For the
58
+ # type `Array`, it returns the values. For any other type
59
+ # that is a class, it tries to initialize the class with the
60
+ # given arguments.
61
+ #
62
+ # @param defined [Boolean] Whether or not the value was
63
+ # actively defined in the grammar.
64
+ # @param values [Array<String>] The values that the directive
65
+ # was defined with.
66
+ # @param type [Object?] The type expected of the arguments
67
+ # given.
68
+ # @return [Object?]
69
+ def coerce_directive_value(defined, values, type)
70
+ return nil unless defined || type.is_a?(Array)
71
+
72
+ case type
73
+ when nil
74
+ values.any? ? values[0] : true
75
+ when :single, :one
76
+ values[0]
77
+ when Boolean
78
+ values[0].to_s != 'false'
79
+ when Array
80
+ values.zip(type).map do |value, t|
81
+ coerce_directive_value(defined, [value], t)
82
+ end
83
+ when Class
84
+ coerce_directive_class(values, type)
85
+ else
86
+ raise UnknownTypeError, "unknown type #{type}"
87
+ end
88
+ end
89
+
90
+ # If the expected of the directive is a Class, then we try to
91
+ # determine which class is expected, and return the proper
92
+ # values.
93
+ #
94
+ # @param values [Array<String>] The values that the directive
95
+ # was defined with.
96
+ # @param type [Class] The type expected of the arguments
97
+ # given.
98
+ # @return [Object]
99
+ def coerce_directive_class(values, type)
100
+ if type == Array
101
+ values
102
+ elsif type == String
103
+ values[0].to_s
104
+ elsif type == Fixnum || type == Integer || type == Numeric
105
+ values[0].to_i
106
+ elsif type == Float
107
+ values[0].to_f
108
+ else
109
+ type.new(*values)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,50 @@
1
+ module Antelope
2
+ module Generator
3
+ class Base
4
+ # Includes some extra processed information about the grammar
5
+ # to be provided to general generators.
6
+ module Extra
7
+ # The actual table that is used for parsing. This returns an
8
+ # array of hashes; the array index corresponds to the state
9
+ # number, and the hash keys correspond to the lookahead tokens.
10
+ # The hash values are an array; the first element of that array
11
+ # is the action to be taken, and the second element of the
12
+ # array is the argument for that action. Possible actions
13
+ # include `:accept`, `:reduce`, and `:state`; `:accept` means
14
+ # to accept the string; `:reduce` means to perform the given
15
+ # reduction; and `:state` means to transition to the given
16
+ # state.
17
+ #
18
+ # @return [Array<Hash<Symbol => Array<(Symbol, Numeric)>>>]
19
+ def table
20
+ if mods[:tableizer].is_a? Generation::Tableizer
21
+ mods[:tableizer].table
22
+ else
23
+ []
24
+ end
25
+ end
26
+
27
+ # Returns an array of the production information of each
28
+ # production needed by the parser. The first element of any
29
+ # element in the array is an {Ace::Token::Nonterminal} that
30
+ # that specific production reduces to; the second element
31
+ # is a number describing the number of items in the right hand
32
+ # side of the production; the string represents the action
33
+ # that should be taken on reduction.
34
+ #
35
+ # This information is used for `:reduce` actions in the parser;
36
+ # the value of the `:reduce` action corresponds to the array
37
+ # index of the production in this array.
38
+ #
39
+ # @return [Array<Array<(Ace::Token::Nonterminal, Numeric, String)>]
40
+ def productions
41
+ grammar.all_productions.map do |production|
42
+ [production[:label],
43
+ production[:items].size,
44
+ production[:block]]
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end