antelope 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +11 -0
  3. data/bin/antelope +3 -3
  4. data/examples/{example.err → example.ace.err} +9 -9
  5. data/examples/{example.inf → example.ace.inf} +57 -9
  6. data/examples/example.ate +70 -0
  7. data/examples/example.ate.err +192 -0
  8. data/examples/example.ate.inf +432 -0
  9. data/lib/antelope/ace/compiler.rb +4 -4
  10. data/lib/antelope/ace/errors.rb +0 -18
  11. data/lib/antelope/ace.rb +6 -12
  12. data/lib/antelope/cli.rb +1 -1
  13. data/lib/antelope/dsl/compiler.rb +117 -0
  14. data/lib/antelope/dsl/contexts/base.rb +29 -0
  15. data/lib/antelope/dsl/contexts/main.rb +63 -0
  16. data/lib/antelope/dsl/contexts/match.rb +24 -0
  17. data/lib/antelope/dsl/contexts/precedence.rb +20 -0
  18. data/lib/antelope/dsl/contexts/production.rb +24 -0
  19. data/lib/antelope/dsl/contexts/terminal.rb +28 -0
  20. data/lib/antelope/dsl/contexts.rb +16 -0
  21. data/lib/antelope/dsl.rb +9 -0
  22. data/lib/antelope/errors.rb +18 -1
  23. data/lib/antelope/generation/constructor/first.rb +10 -12
  24. data/lib/antelope/generation/constructor/follow.rb +6 -6
  25. data/lib/antelope/generation/constructor/nullable.rb +6 -6
  26. data/lib/antelope/generation/constructor.rb +4 -4
  27. data/lib/antelope/generation/recognizer/rule.rb +17 -17
  28. data/lib/antelope/generation/recognizer/state.rb +9 -10
  29. data/lib/antelope/generation/recognizer.rb +8 -11
  30. data/lib/antelope/generation/tableizer.rb +2 -2
  31. data/lib/antelope/generator/base.rb +7 -7
  32. data/lib/antelope/generator/ruby.rb +1 -1
  33. data/lib/antelope/grammar/generation.rb +77 -0
  34. data/lib/antelope/grammar/loading.rb +84 -0
  35. data/lib/antelope/{ace → grammar}/precedence.rb +2 -4
  36. data/lib/antelope/grammar/precedences.rb +64 -0
  37. data/lib/antelope/{ace → grammar}/production.rb +11 -12
  38. data/lib/antelope/grammar/productions.rb +154 -0
  39. data/lib/antelope/grammar/symbols.rb +64 -0
  40. data/lib/antelope/{ace → grammar}/token/epsilon.rb +1 -2
  41. data/lib/antelope/{ace → grammar}/token/error.rb +1 -3
  42. data/lib/antelope/{ace → grammar}/token/nonterminal.rb +1 -3
  43. data/lib/antelope/{ace → grammar}/token/terminal.rb +1 -3
  44. data/lib/antelope/{ace → grammar}/token.rb +12 -15
  45. data/lib/antelope/grammar.rb +68 -0
  46. data/lib/antelope/version.rb +1 -1
  47. data/lib/antelope.rb +12 -6
  48. data/spec/antelope/ace/compiler_spec.rb +6 -6
  49. data/spec/antelope/ace/scanner_spec.rb +7 -7
  50. data/spec/antelope/generation/constructor_spec.rb +131 -0
  51. data/spec/support/grammar_helper.rb +2 -3
  52. metadata +32 -19
  53. data/lib/antelope/ace/grammar/generation.rb +0 -80
  54. data/lib/antelope/ace/grammar/loading.rb +0 -53
  55. data/lib/antelope/ace/grammar/precedences.rb +0 -68
  56. data/lib/antelope/ace/grammar/productions.rb +0 -156
  57. data/lib/antelope/ace/grammar/symbols.rb +0 -66
  58. data/lib/antelope/ace/grammar.rb +0 -69
  59. data/spec/antelope/constructor_spec.rb +0 -133
data/lib/antelope.rb CHANGED
@@ -1,13 +1,19 @@
1
1
  # encoding: utf-8
2
2
 
3
- require "antelope/errors"
4
- require "antelope/generation"
5
- require "antelope/generator"
6
- require "antelope/version"
7
- require "antelope/ace"
8
- require "antelope/template"
3
+ require 'antelope/errors'
4
+ require 'antelope/generation'
5
+ require 'antelope/generator'
6
+ require 'antelope/version'
7
+ require 'antelope/grammar'
8
+ require 'antelope/ace'
9
+ require 'antelope/dsl'
10
+ require 'antelope/template'
9
11
 
10
12
  # Antelope, the compiler compiler.
11
13
  module Antelope
14
+ def self.define(name, options = {}, &block)
15
+ @grammar = [name, options, block]
16
+ end
12
17
 
18
+ class << self; attr_reader :grammar; end
13
19
  end
@@ -43,18 +43,18 @@ describe Ace::Compiler do
43
43
  its(:body) { should =~ /hello/ }
44
44
  its(:options) { should have_key :type }
45
45
 
46
- it "should have the proper terminals" do
46
+ it 'has the proper terminals' do
47
47
  expect(subject.options[:terminals].map(&:first)).to eq [:NUMBER,
48
48
  :SEMICOLON, :ADD, :LPAREN, :RPAREN]
49
49
  end
50
50
 
51
- context "with an unmatched version" do
52
- let :file do "%require \"0.0.0\"\n%%\n%%\n" end
51
+ context 'with an unmatched version' do
52
+ let(:file) { "%require \"0.0.0\"\n%%\n%%\n" }
53
53
 
54
- it "should raise an error" do
55
- expect {
54
+ it 'raises an error' do
55
+ expect do
56
56
  subject
57
- }.to raise_error(Ace::IncompatibleVersionError)
57
+ end.to raise_error(IncompatibleVersionError)
58
58
  end
59
59
  end
60
60
  end
@@ -1,20 +1,20 @@
1
1
  describe Ace::Scanner do
2
2
 
3
- it "properly scans" do
3
+ it 'properly scans' do
4
4
  expect(scan("%test \"a\" hi\n%%\nt: d { { } }\n%%\nhi\n")).to eq [
5
- [:directive, "test", ["a", "hi"]],
5
+ [:directive, 'test', %w(a hi)],
6
6
  [:second],
7
- [:label, "t", nil],
8
- [:part, "d", nil],
9
- [:block, "{ { } }"],
7
+ [:label, 't', nil],
8
+ [:part, 'd', nil],
9
+ [:block, '{ { } }'],
10
10
  [:third],
11
11
  [:copy, "\nhi\n"]
12
12
  ]
13
13
  end
14
14
 
15
- it "throws an error" do
15
+ it 'throws an error' do
16
16
  expect {
17
- scan("% %% %% ")
17
+ scan('% %% %% ')
18
18
  }.to raise_error(Ace::SyntaxError)
19
19
  end
20
20
 
@@ -0,0 +1,131 @@
1
+ describe Generation::Constructor do
2
+ let(:grammar) { double('grammar') }
3
+ let(:terminal) { token(:TERMINAL) }
4
+ let(:epsilon) { token(:epsilon) }
5
+
6
+ subject { described_class.new(grammar) }
7
+
8
+ context '#nullable?' do
9
+ context 'when given an epsilon token' do
10
+ it 'returns true' do
11
+ expect(subject.nullable?(epsilon)).to be true
12
+ end
13
+ end
14
+
15
+ context 'when given a terminal' do
16
+ it 'returns false' do
17
+ expect(subject.nullable?(terminal)).to be false
18
+ end
19
+ end
20
+
21
+ context 'when given an array' do
22
+ context 'with one of the elements not nullable' do
23
+ it 'returns false' do
24
+ expect(subject.nullable?([terminal, epsilon])).to be false
25
+ end
26
+ end
27
+
28
+ context 'with all of the elements nullable' do
29
+ it 'returns true' do
30
+ expect(subject.nullable?([epsilon, epsilon])).to be true
31
+ end
32
+ end
33
+ end
34
+
35
+ context 'when given a nonterminal' do
36
+ let(:grammar) { with_recognizer }
37
+
38
+ context 'with no nullable productions' do
39
+ let(:nonterminal) { Grammar::Token::Nonterminal.new(:l) }
40
+
41
+ it 'returns false' do
42
+ expect(subject.nullable?(nonterminal)).to be false
43
+ end
44
+ end
45
+
46
+ context 'with a nullable production' do
47
+ let(:nonterminal) { Grammar::Token::Nonterminal.new(:e) }
48
+
49
+ it 'returns true' do
50
+ expect(subject.nullable?(nonterminal)).to be true
51
+ end
52
+ end
53
+ end
54
+
55
+ context 'when given a bad argument' do
56
+ it 'raises an error' do
57
+ expect { subject.nullable?(nil) }.to raise_error(ArgumentError)
58
+ end
59
+ end
60
+ end
61
+
62
+ context '#first' do
63
+ context 'when given an epsilon token' do
64
+ it 'generates an empty set' do
65
+ expect(subject.first(epsilon)).to eq Set.new
66
+ end
67
+ end
68
+
69
+ context 'when given a terminal' do
70
+ it 'generates a set' do
71
+ expect(subject.first(terminal)).to eq [terminal].to_set
72
+ end
73
+ end
74
+
75
+ context 'when given an array' do
76
+ let(:terminal2) { token(:terminal, :TERMINAL2) }
77
+
78
+ it 'generates a set' do
79
+ expect(subject.first([epsilon, terminal]))
80
+ .to eq [terminal].to_set
81
+ expect(subject.first([terminal, terminal2]))
82
+ .to eq [terminal].to_set
83
+ end
84
+ end
85
+
86
+ context 'when given a nonterminal' do
87
+ let(:grammar) { with_recognizer }
88
+ let(:nonterminal) { token(:nonterminal, :e) }
89
+
90
+ it 'generates a set' do
91
+ expect(subject.first(nonterminal))
92
+ .to eq [token(:terminal, :IDENT), token(:terminal, :STAR, '*')].to_set
93
+ end
94
+ end
95
+
96
+ context 'when given a bad argument' do
97
+ it 'raises an error' do
98
+ expect { subject.first(nil) }.to raise_error(ArgumentError)
99
+ end
100
+ end
101
+ end
102
+
103
+ context '#follow' do
104
+ context 'when given a bad argument' do
105
+ it 'raises an error' do
106
+ expect { subject.follow(nil) }.to raise_error(ArgumentError)
107
+ end
108
+ end
109
+
110
+ context 'when given a nonterminal' do
111
+ let(:grammar) { with_recognizer }
112
+ let(:nonterminal) { token(:nonterminal, :l) }
113
+
114
+ before do
115
+ subject.productions.merge grammar.productions.values.flatten
116
+ end
117
+
118
+ it 'generates a set' do
119
+ expect(subject.follow(nonterminal)).to eq [
120
+ token(:terminal, :EQUALS, '='),
121
+ token(:terminal, :'$end')
122
+ ].to_set
123
+ end
124
+ end
125
+ end
126
+
127
+ def token(type, name = nil, value = nil, ttype = nil, id = nil)
128
+ type = Grammar::Token.const_get(type.to_s.capitalize)
129
+ type.new(name, ttype, id, value)
130
+ end
131
+ end
@@ -1,13 +1,12 @@
1
1
  module GrammarHelper
2
-
3
2
  def grammar_for(grammar_file = "simple")
4
3
  source_path = Pathname.new("../../fixtures").expand_path(__FILE__)
5
- Ace::Grammar.from_file(source_path.children.select(&:file?)
4
+ Grammar.from_file(source_path.children.select(&:file?)
6
5
  .find { |x| x.to_s =~ /#{Regexp.escape(grammar_file)}\..*\z/ }.to_s)
7
6
  end
8
7
 
9
8
  def with_recognizer(grammar = simple_grammar)
10
- recognizer = Generation::Recognizer.new(grammar).call
9
+ Generation::Recognizer.new(grammar).call
11
10
  grammar
12
11
  end
13
12
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: antelope
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Rodi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-22 00:00:00.000000000 Z
11
+ date: 2015-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hashie
@@ -104,6 +104,7 @@ extra_rdoc_files: []
104
104
  files:
105
105
  - ".gitignore"
106
106
  - ".rspec"
107
+ - ".rubocop.yml"
107
108
  - ".travis.yml"
108
109
  - ".yardopts"
109
110
  - CONTRIBUTING.md
@@ -117,33 +118,32 @@ files:
117
118
  - bin/antelope
118
119
  - examples/deterministic.ace
119
120
  - examples/example.ace
120
- - examples/example.err
121
- - examples/example.inf
121
+ - examples/example.ace.err
122
+ - examples/example.ace.inf
123
+ - examples/example.ate
124
+ - examples/example.ate.err
125
+ - examples/example.ate.inf
122
126
  - examples/liquidscript.ace
123
127
  - examples/simple.ace
124
128
  - lib/antelope.rb
125
129
  - lib/antelope/ace.rb
126
130
  - lib/antelope/ace/compiler.rb
127
131
  - lib/antelope/ace/errors.rb
128
- - lib/antelope/ace/grammar.rb
129
- - lib/antelope/ace/grammar/generation.rb
130
- - lib/antelope/ace/grammar/loading.rb
131
- - lib/antelope/ace/grammar/precedences.rb
132
- - lib/antelope/ace/grammar/productions.rb
133
- - lib/antelope/ace/grammar/symbols.rb
134
- - lib/antelope/ace/precedence.rb
135
- - lib/antelope/ace/production.rb
136
132
  - lib/antelope/ace/scanner.rb
137
133
  - lib/antelope/ace/scanner/argument.rb
138
134
  - lib/antelope/ace/scanner/first.rb
139
135
  - lib/antelope/ace/scanner/second.rb
140
136
  - lib/antelope/ace/scanner/third.rb
141
- - lib/antelope/ace/token.rb
142
- - lib/antelope/ace/token/epsilon.rb
143
- - lib/antelope/ace/token/error.rb
144
- - lib/antelope/ace/token/nonterminal.rb
145
- - lib/antelope/ace/token/terminal.rb
146
137
  - lib/antelope/cli.rb
138
+ - lib/antelope/dsl.rb
139
+ - lib/antelope/dsl/compiler.rb
140
+ - lib/antelope/dsl/contexts.rb
141
+ - lib/antelope/dsl/contexts/base.rb
142
+ - lib/antelope/dsl/contexts/main.rb
143
+ - lib/antelope/dsl/contexts/match.rb
144
+ - lib/antelope/dsl/contexts/precedence.rb
145
+ - lib/antelope/dsl/contexts/production.rb
146
+ - lib/antelope/dsl/contexts/terminal.rb
147
147
  - lib/antelope/errors.rb
148
148
  - lib/antelope/generation.rb
149
149
  - lib/antelope/generation/constructor.rb
@@ -179,6 +179,19 @@ files:
179
179
  - lib/antelope/generator/templates/html/js.ant
180
180
  - lib/antelope/generator/templates/info.ant
181
181
  - lib/antelope/generator/templates/ruby.ant
182
+ - lib/antelope/grammar.rb
183
+ - lib/antelope/grammar/generation.rb
184
+ - lib/antelope/grammar/loading.rb
185
+ - lib/antelope/grammar/precedence.rb
186
+ - lib/antelope/grammar/precedences.rb
187
+ - lib/antelope/grammar/production.rb
188
+ - lib/antelope/grammar/productions.rb
189
+ - lib/antelope/grammar/symbols.rb
190
+ - lib/antelope/grammar/token.rb
191
+ - lib/antelope/grammar/token/epsilon.rb
192
+ - lib/antelope/grammar/token/error.rb
193
+ - lib/antelope/grammar/token/nonterminal.rb
194
+ - lib/antelope/grammar/token/terminal.rb
182
195
  - lib/antelope/template.rb
183
196
  - lib/antelope/template/compiler.rb
184
197
  - lib/antelope/template/errors.rb
@@ -187,7 +200,7 @@ files:
187
200
  - optimizations.txt
188
201
  - spec/antelope/ace/compiler_spec.rb
189
202
  - spec/antelope/ace/scanner_spec.rb
190
- - spec/antelope/constructor_spec.rb
203
+ - spec/antelope/generation/constructor_spec.rb
191
204
  - spec/antelope/template_spec.rb
192
205
  - spec/fixtures/simple.ace
193
206
  - spec/spec_helper.rb
@@ -222,7 +235,7 @@ summary: A compiler compiler, written in ruby.
222
235
  test_files:
223
236
  - spec/antelope/ace/compiler_spec.rb
224
237
  - spec/antelope/ace/scanner_spec.rb
225
- - spec/antelope/constructor_spec.rb
238
+ - spec/antelope/generation/constructor_spec.rb
226
239
  - spec/antelope/template_spec.rb
227
240
  - spec/fixtures/simple.ace
228
241
  - spec/spec_helper.rb
@@ -1,80 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Antelope
4
- module Ace
5
- class Grammar
6
-
7
- # The default modifiers for generation. It's not really
8
- # recommended to (heh) modify this; however, adding your own
9
- # modifier is always acceptable.
10
- DEFAULT_MODIFIERS = [
11
- [:recognizer, Generation::Recognizer ],
12
- [:constructor, Generation::Constructor],
13
- [:tableizer, Generation::Tableizer ]
14
- ].freeze
15
-
16
- # Handles the generation of output for the grammar.
17
- module Generation
18
-
19
- # Generates the output. First, it runs through every given
20
- # modifier, and instintates it. It then calls every modifier,
21
- # turns it into a hash, and passes that hash to each of the
22
- # given generators.
23
- #
24
- # @param options [Hash] options.
25
- # @param generators [Array<Generator>] a list of generators
26
- # to use in generation.
27
- # @param modifiers [Array<Array<(Symbol, #call)>>] a list of
28
- # modifiers to apply to the grammar.
29
- # @return [void]
30
- def generate(options = {},
31
- generators = :guess,
32
- modifiers = DEFAULT_MODIFIERS)
33
- mods = modifiers.map(&:last).
34
- map { |x| x.new(self) }
35
- mods.each do |mod|
36
- puts "Running mod #{mod.class}..." if options[:verbose]
37
- mod.call
38
- end
39
- hash = Hash[modifiers.map(&:first).zip(mods)]
40
- # This is when we'd generate
41
-
42
- find_generators(generators, options).each do |gen|
43
- puts "Running generator #{gen}..." if options[:verbose]
44
- gen.new(self, hash).generate
45
- end
46
- end
47
-
48
- private
49
-
50
- # Find the corresponding generators. If the first argument
51
- # isn't `:guess`, it returns the first argument. Otherwise,
52
- # it tries to "intelligently guess" by checking the type from
53
- # the options _or_ the compiler. If it is unable to find the
54
- # type, it will raise a {NoTypeError}.
55
- #
56
- # @raise [NoTypeError] if it could not determine the type of
57
- # the generator.
58
- # @param generators [Symbol, Array<Generator>]
59
- # @param options [Hash]
60
- # @return [Array<Generator>]
61
- def find_generators(generators, options)
62
- return generators unless generators == :guess
63
-
64
- generators = [Generator::Output]
65
-
66
- # command line precedence...
67
- type = options[:type] || options["type"] ||
68
- compiler.options.fetch(:type)
69
-
70
- generators << Generator.generators.fetch(type)
71
-
72
- generators
73
-
74
- rescue KeyError
75
- raise NoTypeError, "Undefined type #{type}"
76
- end
77
- end
78
- end
79
- end
80
- end
@@ -1,53 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Antelope
4
- module Ace
5
- class Grammar
6
-
7
- # Handles loading to and from files and strings.
8
- module Loading
9
-
10
- # Defines class methods on the grammar.
11
- module ClassMethods
12
-
13
- # Loads a grammar from a file. Assumes the output
14
- # directory and name from the file name.
15
- #
16
- # @param file_name [String] the file name.
17
- # @return [Grammar]
18
- # @see #from_string
19
- def from_file(file_name)
20
- body = File.read(file_name)
21
- output = File.dirname(file_name)
22
- name = File.basename(file_name).gsub(/\.[A-Za-z]+/, "")
23
- from_string(name, output, body)
24
- end
25
-
26
- # Loads a grammar from a string. First runs the scanner and
27
- # compiler over the string, and then instantiates a new
28
- # Grammar from the resultant.
29
- #
30
- # @param name [String] the name of the grammar.
31
- # @param output [String] the output directory.
32
- # @param string [String] the grammar body.
33
- # @return [Grammar]
34
- # @see Ace::Scanner
35
- # @see Ace::Compiler
36
- def from_string(name, output, string)
37
- scanner = Ace::Scanner.scan(string, name)
38
- compiler = Ace::Compiler.compile(scanner)
39
- new(name, output, compiler)
40
- end
41
- end
42
-
43
- # Extends the grammar with the class methods.
44
- #
45
- # @param receiver [Grammar]
46
- # @see ClassMethods
47
- def self.included(receiver)
48
- receiver.extend ClassMethods
49
- end
50
- end
51
- end
52
- end
53
- end
@@ -1,68 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require "set"
4
-
5
- module Antelope
6
- module Ace
7
- class Grammar
8
-
9
- # Manages precedence for tokens.
10
- module Precedences
11
-
12
- # Accesses the generated precedence list. Lazily generates
13
- # the precedence rules on the go, and then caches it.
14
- #
15
- # @return [Array<Ace::Precedence>]
16
- def precedence
17
- @_precedence ||= generate_precedence
18
- end
19
-
20
- # Finds a precedence rule for a given token. If no direct
21
- # rule is defined for that token, it will check for a rule
22
- # defined for the special symbol, `:_`. By default, there
23
- # is always a rule defined for `:_`.
24
- #
25
- # @param token [Ace::Token, Symbol]
26
- # @return [Ace::Precedence]
27
- def precedence_for(token)
28
- token = token.name if token.is_a?(Token)
29
-
30
- prec = precedence.
31
- detect { |pr| pr.tokens.include?(token) } ||
32
- precedence.
33
- detect { |pr| pr.tokens.include?(:_) }
34
-
35
- prec
36
- end
37
-
38
- private
39
-
40
- # Generates the precedence rules. Loops through the compiler
41
- # given precedence settings, and then adds two default
42
- # precedence rules; one for `:$` (level 0, nonassoc), and one
43
- # for `:_` (level 1, nonassoc).
44
- #
45
- # @return [Array<Ace::Precedence>]
46
- def generate_precedence
47
- size = @compiler.options[:prec].size + 1
48
- index = 0
49
- precedence = []
50
-
51
- while index < size - 1
52
- prec = @compiler.options[:prec][index]
53
- precedence <<
54
- Ace::Precedence.new(prec[0], prec[1..-1].to_set,
55
- size - index)
56
- index += 1
57
- end
58
-
59
- precedence <<
60
- Ace::Precedence.new(:nonassoc, [:$end].to_set, 0) <<
61
- Ace::Precedence.new(:nonassoc, [:_].to_set, 1)
62
- precedence.sort_by(&:level).reverse
63
- end
64
-
65
- end
66
- end
67
- end
68
- end