antelope 0.2.4 → 0.3.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 (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
@@ -1,156 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Antelope
4
- module Ace
5
- class Grammar
6
-
7
- # Manages the productions of the grammar.
8
- module Productions
9
-
10
- # Returns a hash of all of the productions. The result is
11
- # cached.
12
- #
13
- # @return [Hash<(Symbol, Array<Production>)>]
14
- def productions
15
- @_productions || generate_productions
16
- end
17
-
18
- # Returns all productions for all nonterminals, sorted by id.
19
- #
20
- # @return [Array<Production>]
21
- def all_productions
22
- productions.values.flatten.sort_by(&:id)
23
- end
24
-
25
- # Finds a token based on its corresponding symbol. First
26
- # checks the productions, to see if it's a nonterminal; then,
27
- # tries to find it in the terminals; otherwise, if the symbol
28
- # is `error`, it returns a {Token::Error}; if the symbol is
29
- # `nothing` or `ε`, it returns a {Token::Epsilon}; if it's
30
- # none of those, it raises an {UndefinedTokenError}.
31
- #
32
- # @raise [UndefinedTokenError] if the token doesn't exist.
33
- # @param value [String, Symbol, #intern] the token's symbol to
34
- # check.
35
- # @return [Token]
36
- def find_token(value)
37
- value = value.intern
38
-
39
- if productions.key?(value)
40
- typed_nonterminals.find { |term| term.name == value } ||
41
- Token::Nonterminal.new(value)
42
- elsif terminal = terminals.
43
- find { |term| term.name == value }
44
- terminal
45
- elsif value == :error
46
- Token::Error.new
47
- elsif [:nothing, :ε, :"%empty"].include?(value)
48
- Token::Epsilon.new
49
- else
50
- raise UndefinedTokenError, "Could not find a token " \
51
- "named #{value.inspect}"
52
- end
53
- end
54
-
55
- private
56
-
57
- # Actually generates the productions. Uses the rules from the
58
- # compiler to construct the productions. Makes two loops over
59
- # the compiler's rules; the first to tell the grammar that the
60
- # nonterminal does exist, and the second to actually construct
61
- # the productions. The first loop is for {#find_token},
62
- # because otherwise it wouldn't be able to return a
63
- # nonterminal properly.
64
- #
65
- # @return [Hash<(Symbol, Array<Production>)>]
66
- def generate_productions
67
- @_productions = {}
68
- index = 0
69
-
70
- rules = @compiler.rules.each do |rule|
71
- productions[rule[:label]] = []
72
- end
73
-
74
- while index < rules.size
75
- rule = rules[index]
76
- productions[rule[:label]] <<
77
- generate_production_for(rule, index)
78
- index += 1
79
- end
80
-
81
- productions[:$start] = [default_production]
82
-
83
- productions
84
- end
85
-
86
- # Generates a production for a given compiler rule. Converts
87
- # the tokens in the set to their {Token} counterparts,
88
- # and then sets the precedence for the production. If the
89
- # precedence declaration from the compiler rule is empty,
90
- # then it'll use the last terminal from the set to check for
91
- # precedence; otherwise, it'll use the precedence declaration.
92
- # This is to make sure that every production has a precedence
93
- # declaration.
94
- #
95
- # @param rule [Hash] the compiler's rule.
96
- # @param id [Numeric] the id for the production.
97
- # @return [Production]
98
- def generate_production_for(rule, id)
99
- left = Token::Nonterminal.new(rule[:label])
100
- items = rule[:set].map { |_| find_token(_[0]) }
101
- prec = if rule[:prec].empty?
102
- items.select(&:terminal?).first
103
- else
104
- rule[:prec].intern
105
- end
106
-
107
- prec = precedence_for(prec)
108
- left.type = type_for(rule[:label])
109
- left.id = rule[:label_id]
110
-
111
- rule[:set].each_with_index do |tok, i|
112
- items[i] = items[i].dup
113
- items[i].id = tok[1]
114
- end
115
- items.delete_if(&:epsilon?)
116
-
117
- Production.new(left, items, rule[:block], prec, id + 1)
118
- end
119
-
120
- # Returns the defined type for the given token name.
121
- # Uses the `%type` directive to infer the corresponding types.
122
- #
123
- # @param token [Symbol] the token to check for
124
- # types.
125
- def type_for(token)
126
- token = find_token(token) unless token.is_a?(Token)
127
-
128
- case token
129
- when Token::Nonterminal
130
- token.type
131
- when Token::Terminal
132
- token.type
133
- when Token::Epsilon
134
- ""
135
- when Token::Error
136
- ""
137
- end
138
- end
139
-
140
- # Creates the default production for the grammar. The left
141
- # hand side of the production is the `:$start` symbol, with
142
- # the right hand side being the first rule's left-hand side
143
- # and the terminal `$`. This production is automagically
144
- # given the last precedence, and an id of 0.
145
- #
146
- # @return [Production]
147
- def default_production
148
- Production.new(Token::Nonterminal.new(:$start), [
149
- Token::Nonterminal.new(@compiler.rules.first[:label]),
150
- Token::Terminal.new(:$end)
151
- ], "", precedence.last, 0)
152
- end
153
- end
154
- end
155
- end
156
- end
@@ -1,66 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Antelope
4
- module Ace
5
- class Grammar
6
-
7
- # Manages a list of the symbols in the grammar.
8
- module Symbols
9
-
10
- # A list of all terminals in the grammar. Checks the compiler
11
- # options for terminals, and then returns an array of
12
- # terminals. Caches the result.
13
- #
14
- # @return [Array<Token::Terminal>]
15
- def terminals
16
- @_terminals ||= begin
17
- @compiler.options.fetch(:terminals) { [] }.map do |v|
18
- Token::Terminal.new(*v)
19
- end
20
- end
21
- end
22
-
23
- # A list of all nonterminals in the grammar.
24
- #
25
- # @return [Array<Symbol>]
26
- # @see #productions
27
- def nonterminals
28
- @_nonterminals ||= productions.keys
29
- end
30
-
31
- # A list of all nonterminals, with types.
32
- #
33
- # @return [Array<Token::Nonterminal>>]
34
- def typed_nonterminals
35
- @_typed_nonterminals ||= begin
36
- typed = []
37
- compiler.options[:nonterminals].each do |data|
38
- data[1].each do |nonterm|
39
- typed << Token::Nonterminal.new(nonterm, data[0])
40
- end
41
- end
42
- typed
43
- end
44
- end
45
-
46
- # A list of all symbols in the grammar; includes both
47
- # terminals and nonterminals.
48
- #
49
- # @return [Array<Token::Terminal, Symbol>]
50
- # @see #terminals
51
- # @see #nonterminals
52
- def symbols
53
- @_symbols ||= terminals + nonterminals
54
- end
55
-
56
- # Checks to see if the grammar uses the `error` terminal
57
- # anywhere.
58
- #
59
- # @return [Boolean]
60
- def contains_error_token?
61
- all_productions.any? { |_| _.items.any?(&:error?) }
62
- end
63
- end
64
- end
65
- end
66
- end
@@ -1,69 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require "hashie"
4
- require "antelope/ace/grammar/symbols"
5
- require "antelope/ace/grammar/productions"
6
- require "antelope/ace/grammar/precedences"
7
- require "antelope/ace/grammar/loading"
8
- require "antelope/ace/grammar/generation"
9
-
10
- module Antelope
11
- module Ace
12
-
13
- # Defines a grammar from an Ace file. This handles setting up
14
- # productions, loading from files, symbols, precedence, and
15
- # generation.
16
- class Grammar
17
-
18
- include Symbols
19
- include Productions
20
- include Precedences
21
- include Loading
22
- include Grammar::Generation
23
-
24
- # Used by a generation class; this is all the generated states
25
- # of the grammar.
26
- #
27
- # @return [Set<Generation::Recognizer::State>]
28
- # @see Generation::Recognizer
29
- attr_accessor :states
30
-
31
- # The name of the grammar. This is normally assumed from a file
32
- # name.
33
- #
34
- # @return [String]
35
- attr_accessor :name
36
-
37
- # The output directory for the grammar. This is normally the
38
- # same directory as the Ace file.
39
- #
40
- # @return [Pathname]
41
- attr_accessor :output
42
-
43
- # The compiler for the Ace file.
44
- #
45
- # @return [Compiler]
46
- attr_reader :compiler
47
-
48
- # Initialize.
49
- #
50
- # @param name [String]
51
- # @param output [String] the output directory. Automagically
52
- # turned into a Pathname.
53
- # @param compiler [Compiler]
54
- def initialize(name, output, compiler)
55
- @name = name
56
- @output = Pathname.new(output)
57
- @compiler = compiler
58
- end
59
-
60
- # Extra options from the compiler. This can be used by
61
- # generators for output information.
62
- #
63
- # @return [Hash]
64
- def options
65
- compiler.options[:extra]
66
- end
67
- end
68
- end
69
- end
@@ -1,133 +0,0 @@
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) { Ace::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) { Ace::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
-
128
-
129
- def token(type, name = nil, value = nil, ttype = nil, id = nil)
130
- type = Ace::Token.const_get(type.to_s.capitalize)
131
- type.new(name, ttype, id, value)
132
- end
133
- end