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