antelope 0.2.4 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +11 -0
- data/bin/antelope +3 -3
- data/examples/{example.err → example.ace.err} +9 -9
- data/examples/{example.inf → example.ace.inf} +57 -9
- data/examples/example.ate +70 -0
- data/examples/example.ate.err +192 -0
- data/examples/example.ate.inf +432 -0
- data/lib/antelope/ace/compiler.rb +4 -4
- data/lib/antelope/ace/errors.rb +0 -18
- data/lib/antelope/ace.rb +6 -12
- data/lib/antelope/cli.rb +1 -1
- data/lib/antelope/dsl/compiler.rb +117 -0
- data/lib/antelope/dsl/contexts/base.rb +29 -0
- data/lib/antelope/dsl/contexts/main.rb +63 -0
- data/lib/antelope/dsl/contexts/match.rb +24 -0
- data/lib/antelope/dsl/contexts/precedence.rb +20 -0
- data/lib/antelope/dsl/contexts/production.rb +24 -0
- data/lib/antelope/dsl/contexts/terminal.rb +28 -0
- data/lib/antelope/dsl/contexts.rb +16 -0
- data/lib/antelope/dsl.rb +9 -0
- data/lib/antelope/errors.rb +18 -1
- data/lib/antelope/generation/constructor/first.rb +10 -12
- data/lib/antelope/generation/constructor/follow.rb +6 -6
- data/lib/antelope/generation/constructor/nullable.rb +6 -6
- data/lib/antelope/generation/constructor.rb +4 -4
- data/lib/antelope/generation/recognizer/rule.rb +17 -17
- data/lib/antelope/generation/recognizer/state.rb +9 -10
- data/lib/antelope/generation/recognizer.rb +8 -11
- data/lib/antelope/generation/tableizer.rb +2 -2
- data/lib/antelope/generator/base.rb +7 -7
- data/lib/antelope/generator/ruby.rb +1 -1
- data/lib/antelope/grammar/generation.rb +77 -0
- data/lib/antelope/grammar/loading.rb +84 -0
- data/lib/antelope/{ace → grammar}/precedence.rb +2 -4
- data/lib/antelope/grammar/precedences.rb +64 -0
- data/lib/antelope/{ace → grammar}/production.rb +11 -12
- data/lib/antelope/grammar/productions.rb +154 -0
- data/lib/antelope/grammar/symbols.rb +64 -0
- data/lib/antelope/{ace → grammar}/token/epsilon.rb +1 -2
- data/lib/antelope/{ace → grammar}/token/error.rb +1 -3
- data/lib/antelope/{ace → grammar}/token/nonterminal.rb +1 -3
- data/lib/antelope/{ace → grammar}/token/terminal.rb +1 -3
- data/lib/antelope/{ace → grammar}/token.rb +12 -15
- data/lib/antelope/grammar.rb +68 -0
- data/lib/antelope/version.rb +1 -1
- data/lib/antelope.rb +12 -6
- data/spec/antelope/ace/compiler_spec.rb +6 -6
- data/spec/antelope/ace/scanner_spec.rb +7 -7
- data/spec/antelope/generation/constructor_spec.rb +131 -0
- data/spec/support/grammar_helper.rb +2 -3
- metadata +32 -19
- data/lib/antelope/ace/grammar/generation.rb +0 -80
- data/lib/antelope/ace/grammar/loading.rb +0 -53
- data/lib/antelope/ace/grammar/precedences.rb +0 -68
- data/lib/antelope/ace/grammar/productions.rb +0 -156
- data/lib/antelope/ace/grammar/symbols.rb +0 -66
- data/lib/antelope/ace/grammar.rb +0 -69
- data/spec/antelope/constructor_spec.rb +0 -133
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Antelope
|
4
|
+
class Grammar
|
5
|
+
# The default modifiers for generation. It's not really
|
6
|
+
# recommended to (heh) modify this; however, adding your own
|
7
|
+
# modifier is always acceptable.
|
8
|
+
DEFAULT_MODIFIERS = [
|
9
|
+
[:recognizer, Generation::Recognizer ],
|
10
|
+
[:constructor, Generation::Constructor],
|
11
|
+
[:tableizer, Generation::Tableizer ]
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
# Handles the generation of output for the grammar.
|
15
|
+
module Generation
|
16
|
+
# Generates the output. First, it runs through every given
|
17
|
+
# modifier, and instintates it. It then calls every modifier,
|
18
|
+
# turns it into a hash, and passes that hash to each of the
|
19
|
+
# given generators.
|
20
|
+
#
|
21
|
+
# @param options [Hash] options.
|
22
|
+
# @param generators [Array<Generator>] a list of generators
|
23
|
+
# to use in generation.
|
24
|
+
# @param modifiers [Array<Array<(Symbol, #call)>>] a list of
|
25
|
+
# modifiers to apply to the grammar.
|
26
|
+
# @return [void]
|
27
|
+
def generate(options = {},
|
28
|
+
generators = :guess,
|
29
|
+
modifiers = DEFAULT_MODIFIERS)
|
30
|
+
pp self
|
31
|
+
mods = modifiers.map(&:last)
|
32
|
+
.map { |x| x.new(self) }
|
33
|
+
mods.each do |mod|
|
34
|
+
puts "Running mod #{mod.class}..." if options[:verbose]
|
35
|
+
mod.call
|
36
|
+
end
|
37
|
+
hash = Hash[modifiers.map(&:first).zip(mods)]
|
38
|
+
# This is when we'd generate
|
39
|
+
|
40
|
+
find_generators(generators, options).each do |gen|
|
41
|
+
puts "Running generator #{gen}..." if options[:verbose]
|
42
|
+
gen.new(self, hash).generate
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# Find the corresponding generators. If the first argument
|
49
|
+
# isn't `:guess`, it returns the first argument. Otherwise,
|
50
|
+
# it tries to "intelligently guess" by checking the type from
|
51
|
+
# the options _or_ the compiler. If it is unable to find the
|
52
|
+
# type, it will raise a {NoTypeError}.
|
53
|
+
#
|
54
|
+
# @raise [NoTypeError] if it could not determine the type of
|
55
|
+
# the generator.
|
56
|
+
# @param generators [Symbol, Array<Generator>]
|
57
|
+
# @param options [Hash]
|
58
|
+
# @return [Array<Generator>]
|
59
|
+
def find_generators(generators, options)
|
60
|
+
return generators unless generators == :guess
|
61
|
+
|
62
|
+
generators = [Generator::Output]
|
63
|
+
|
64
|
+
# command line precedence...
|
65
|
+
type = options[:type] || options['type'] ||
|
66
|
+
compiler.options.fetch(:type)
|
67
|
+
|
68
|
+
generators << Generator.generators.fetch(type.to_s)
|
69
|
+
|
70
|
+
generators
|
71
|
+
|
72
|
+
rescue KeyError
|
73
|
+
raise NoTypeError, "Undefined type #{type}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Antelope
|
4
|
+
class Grammar
|
5
|
+
# Handles loading to and from files and strings.
|
6
|
+
module Loading
|
7
|
+
# Defines class methods on the grammar.
|
8
|
+
module ClassMethods
|
9
|
+
# Loads a grammar from a file. Assumes the output
|
10
|
+
# directory and name from the file name.
|
11
|
+
#
|
12
|
+
# @param file_name [String] the file name.
|
13
|
+
# @return [Grammar]
|
14
|
+
# @see #from_string
|
15
|
+
def from_file(file_name)
|
16
|
+
ext = File.extname(file_name)
|
17
|
+
case ext
|
18
|
+
when ".rb", ".ate"
|
19
|
+
from_dsl_file(file_name)
|
20
|
+
when ".ace"
|
21
|
+
from_ace_file(file_name)
|
22
|
+
else
|
23
|
+
raise ArgumentError, "Unexpected file extension #{ext},"\
|
24
|
+
" expected one of .rb, .ate, or .ace"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def from_dsl_file(file_name)
|
29
|
+
body = File.read(file_name)
|
30
|
+
output = File.dirname(file_name)
|
31
|
+
from_dsl_string(file_name, output, body)
|
32
|
+
end
|
33
|
+
|
34
|
+
def from_ace_file(file_name)
|
35
|
+
body = File.read(file_name)
|
36
|
+
output = File.dirname(file_name)
|
37
|
+
name = File.basename(file_name)
|
38
|
+
from_ace_string(name, output, body)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Loads a grammar from a string. First runs the scanner and
|
42
|
+
# compiler over the string, and then instantiates a new
|
43
|
+
# Grammar from the resultant.
|
44
|
+
#
|
45
|
+
# @param file [String] the path of the grammar. This is
|
46
|
+
# used for eval.
|
47
|
+
# @param output [String] the output directory.
|
48
|
+
# @param string [String] the grammar body.
|
49
|
+
# @return [Grammar]
|
50
|
+
# @see DSL::Compiler
|
51
|
+
def from_dsl_string(file, output, string)
|
52
|
+
eval(string, TOPLEVEL_BINDING, file, 0)
|
53
|
+
grammar = Antelope.grammar
|
54
|
+
compiler = DSL::Compiler.compile(grammar[1], &grammar[2])
|
55
|
+
new(File.basename(file), output, compiler)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Loads a grammar from a string. First runs the scanner and
|
59
|
+
# compiler over the string, and then instantiates a new
|
60
|
+
# Grammar from the resultant.
|
61
|
+
#
|
62
|
+
# @param name [String] the name of the grammar.
|
63
|
+
# @param output [String] the output directory.
|
64
|
+
# @param string [String] the grammar body.
|
65
|
+
# @return [Grammar]
|
66
|
+
# @see Ace::Scanner
|
67
|
+
# @see Ace::Compiler
|
68
|
+
def from_ace_string(name, output, string)
|
69
|
+
scanner = Ace::Scanner.scan(string, name)
|
70
|
+
compiler = Ace::Compiler.compile(scanner)
|
71
|
+
new(name, output, compiler)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Extends the grammar with the class methods.
|
76
|
+
#
|
77
|
+
# @param receiver [Grammar]
|
78
|
+
# @see ClassMethods
|
79
|
+
def self.included(receiver)
|
80
|
+
receiver.extend ClassMethods
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -1,12 +1,10 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Antelope
|
4
|
-
|
5
|
-
|
4
|
+
class Grammar
|
6
5
|
# Defines a precedence. A precedence has a type, tokens, and a
|
7
6
|
# level.
|
8
|
-
|
9
|
-
|
7
|
+
Precedence = Struct.new(:type, :tokens, :level) do
|
10
8
|
# @!attribute [rw] type
|
11
9
|
# The type of precedence level. This should be one of
|
12
10
|
# `:left`, `:right`, or `:nonassoc`.
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
module Antelope
|
6
|
+
class Grammar
|
7
|
+
# Manages precedence for tokens.
|
8
|
+
module Precedences
|
9
|
+
# Accesses the generated precedence list. Lazily generates
|
10
|
+
# the precedence rules on the go, and then caches it.
|
11
|
+
#
|
12
|
+
# @return [Array<Ace::Precedence>]
|
13
|
+
def precedence
|
14
|
+
@_precedence ||= generate_precedence
|
15
|
+
end
|
16
|
+
|
17
|
+
# Finds a precedence rule for a given token. If no direct
|
18
|
+
# rule is defined for that token, it will check for a rule
|
19
|
+
# defined for the special symbol, `:_`. By default, there
|
20
|
+
# is always a rule defined for `:_`.
|
21
|
+
#
|
22
|
+
# @param token [Ace::Token, Symbol]
|
23
|
+
# @return [Ace::Precedence]
|
24
|
+
def precedence_for(token)
|
25
|
+
token = token.name if token.is_a?(Token)
|
26
|
+
|
27
|
+
prec = precedence.
|
28
|
+
detect { |pr| pr.tokens.include?(token) } ||
|
29
|
+
precedence.
|
30
|
+
detect { |pr| pr.tokens.include?(:_) }
|
31
|
+
|
32
|
+
prec
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Generates the precedence rules. Loops through the compiler
|
38
|
+
# given precedence settings, and then adds two default
|
39
|
+
# precedence rules; one for `:$` (level 0, nonassoc), and one
|
40
|
+
# for `:_` (level 1, nonassoc).
|
41
|
+
#
|
42
|
+
# @return [Array<Ace::Precedence>]
|
43
|
+
def generate_precedence
|
44
|
+
size = @compiler.options[:prec].size + 1
|
45
|
+
index = 0
|
46
|
+
precedence = []
|
47
|
+
|
48
|
+
while index < size - 1
|
49
|
+
prec = @compiler.options[:prec][index]
|
50
|
+
precedence <<
|
51
|
+
Precedence.new(prec[0], prec[1..-1].to_set,
|
52
|
+
size - index)
|
53
|
+
index += 1
|
54
|
+
end
|
55
|
+
|
56
|
+
precedence <<
|
57
|
+
Precedence.new(:nonassoc, [:$end].to_set, 0) <<
|
58
|
+
Precedence.new(:nonassoc, [:_].to_set, 1)
|
59
|
+
precedence.sort_by(&:level).reverse
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -1,10 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Antelope
|
4
|
-
|
5
|
-
|
4
|
+
class Grammar
|
6
5
|
# Defines a production.
|
7
|
-
|
6
|
+
Production = Struct.new(:label, :items, :block, :prec, :id) do
|
8
7
|
# @!attribute [rw] label
|
9
8
|
# The label (or left-hand side) of the production. This
|
10
9
|
# should be a nonterminal.
|
@@ -35,11 +34,11 @@ module Antelope
|
|
35
34
|
#
|
36
35
|
# @param hash [Hash<(Symbol, Object)>]
|
37
36
|
def self.from_hash(hash)
|
38
|
-
new(hash[:label] || hash[
|
39
|
-
hash[:items] || hash[
|
40
|
-
hash[:block] || hash[
|
41
|
-
hash[:prec] || hash[
|
42
|
-
hash[:id] || hash[
|
37
|
+
new(hash[:label] || hash['label'],
|
38
|
+
hash[:items] || hash['items'],
|
39
|
+
hash[:block] || hash['block'],
|
40
|
+
hash[:prec] || hash['prec'],
|
41
|
+
hash[:id] || hash['id'])
|
43
42
|
end
|
44
43
|
|
45
44
|
# Create a new version of the production with duplicated values.
|
@@ -47,10 +46,10 @@ module Antelope
|
|
47
46
|
# @return [Production]
|
48
47
|
def clone
|
49
48
|
Production.new(label.dup,
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
49
|
+
items.map(&:dup),
|
50
|
+
block.dup,
|
51
|
+
prec.dup,
|
52
|
+
id)
|
54
53
|
end
|
55
54
|
end
|
56
55
|
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Antelope
|
4
|
+
class Grammar
|
5
|
+
|
6
|
+
# Manages the productions of the grammar.
|
7
|
+
module Productions
|
8
|
+
|
9
|
+
# Returns a hash of all of the productions. The result is
|
10
|
+
# cached.
|
11
|
+
#
|
12
|
+
# @return [Hash<(Symbol, Array<Production>)>]
|
13
|
+
def productions
|
14
|
+
@_productions || generate_productions
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns all productions for all nonterminals, sorted by id.
|
18
|
+
#
|
19
|
+
# @return [Array<Production>]
|
20
|
+
def all_productions
|
21
|
+
productions.values.flatten.sort_by(&:id)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Finds a token based on its corresponding symbol. First
|
25
|
+
# checks the productions, to see if it's a nonterminal; then,
|
26
|
+
# tries to find it in the terminals; otherwise, if the symbol
|
27
|
+
# is `error`, it returns a {Token::Error}; if the symbol is
|
28
|
+
# `nothing` or `ε`, it returns a {Token::Epsilon}; if it's
|
29
|
+
# none of those, it raises an {UndefinedTokenError}.
|
30
|
+
#
|
31
|
+
# @raise [UndefinedTokenError] if the token doesn't exist.
|
32
|
+
# @param value [String, Symbol, #intern] the token's symbol to
|
33
|
+
# check.
|
34
|
+
# @return [Token]
|
35
|
+
def find_token(value)
|
36
|
+
value = value.intern
|
37
|
+
|
38
|
+
if productions.key?(value)
|
39
|
+
typed_nonterminals.find { |term| term.name == value } ||
|
40
|
+
Token::Nonterminal.new(value)
|
41
|
+
elsif terminal = terminals.
|
42
|
+
find { |term| term.name == value }
|
43
|
+
terminal
|
44
|
+
elsif value == :$error || value == :error
|
45
|
+
Token::Error.new
|
46
|
+
elsif [:nothing, :ε, :"%empty"].include?(value)
|
47
|
+
Token::Epsilon.new
|
48
|
+
else
|
49
|
+
raise UndefinedTokenError, "Could not find a token " \
|
50
|
+
"named #{value.inspect}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Actually generates the productions. Uses the rules from the
|
57
|
+
# compiler to construct the productions. Makes two loops over
|
58
|
+
# the compiler's rules; the first to tell the grammar that the
|
59
|
+
# nonterminal does exist, and the second to actually construct
|
60
|
+
# the productions. The first loop is for {#find_token},
|
61
|
+
# because otherwise it wouldn't be able to return a
|
62
|
+
# nonterminal properly.
|
63
|
+
#
|
64
|
+
# @return [Hash<(Symbol, Array<Production>)>]
|
65
|
+
def generate_productions
|
66
|
+
@_productions = {}
|
67
|
+
index = 0
|
68
|
+
|
69
|
+
rules = @compiler.rules.each do |rule|
|
70
|
+
productions[rule[:label]] = []
|
71
|
+
end
|
72
|
+
|
73
|
+
while index < rules.size
|
74
|
+
rule = rules[index]
|
75
|
+
productions[rule[:label]] <<
|
76
|
+
generate_production_for(rule, index)
|
77
|
+
index += 1
|
78
|
+
end
|
79
|
+
|
80
|
+
productions[:$start] = [default_production]
|
81
|
+
|
82
|
+
productions
|
83
|
+
end
|
84
|
+
|
85
|
+
# Generates a production for a given compiler rule. Converts
|
86
|
+
# the tokens in the set to their {Token} counterparts,
|
87
|
+
# and then sets the precedence for the production. If the
|
88
|
+
# precedence declaration from the compiler rule is empty,
|
89
|
+
# then it'll use the last terminal from the set to check for
|
90
|
+
# precedence; otherwise, it'll use the precedence declaration.
|
91
|
+
# This is to make sure that every production has a precedence
|
92
|
+
# declaration.
|
93
|
+
#
|
94
|
+
# @param rule [Hash] the compiler's rule.
|
95
|
+
# @param id [Numeric] the id for the production.
|
96
|
+
# @return [Production]
|
97
|
+
def generate_production_for(rule, id)
|
98
|
+
left = Token::Nonterminal.new(rule[:label])
|
99
|
+
items = rule[:set].map { |_| find_token(_[0]) }
|
100
|
+
prec = if rule[:prec].empty?
|
101
|
+
items.select(&:terminal?).first
|
102
|
+
else
|
103
|
+
rule[:prec].intern
|
104
|
+
end
|
105
|
+
|
106
|
+
prec = precedence_for(prec)
|
107
|
+
left.type = type_for(rule[:label])
|
108
|
+
left.id = rule[:label_id]
|
109
|
+
|
110
|
+
rule[:set].each_with_index do |tok, i|
|
111
|
+
items[i] = items[i].dup
|
112
|
+
items[i].id = tok[1]
|
113
|
+
end
|
114
|
+
items.delete_if(&:epsilon?)
|
115
|
+
|
116
|
+
Production.new(left, items, rule[:block], prec, id + 1)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns the defined type for the given token name.
|
120
|
+
# Uses the `%type` directive to infer the corresponding types.
|
121
|
+
#
|
122
|
+
# @param token [Symbol] the token to check for
|
123
|
+
# types.
|
124
|
+
def type_for(token)
|
125
|
+
token = find_token(token) unless token.is_a?(Token)
|
126
|
+
|
127
|
+
case token
|
128
|
+
when Token::Nonterminal
|
129
|
+
token.type
|
130
|
+
when Token::Terminal
|
131
|
+
token.type
|
132
|
+
when Token::Epsilon
|
133
|
+
""
|
134
|
+
when Token::Error
|
135
|
+
""
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Creates the default production for the grammar. The left
|
140
|
+
# hand side of the production is the `:$start` symbol, with
|
141
|
+
# the right hand side being the first rule's left-hand side
|
142
|
+
# and the terminal `$`. This production is automagically
|
143
|
+
# given the last precedence, and an id of 0.
|
144
|
+
#
|
145
|
+
# @return [Production]
|
146
|
+
def default_production
|
147
|
+
Production.new(Token::Nonterminal.new(:$start), [
|
148
|
+
Token::Nonterminal.new(@compiler.rules.first[:label]),
|
149
|
+
Token::Terminal.new(:$end)
|
150
|
+
], "", precedence.last, 0)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Antelope
|
4
|
+
class Grammar
|
5
|
+
|
6
|
+
# Manages a list of the symbols in the grammar.
|
7
|
+
module Symbols
|
8
|
+
|
9
|
+
# A list of all terminals in the grammar. Checks the compiler
|
10
|
+
# options for terminals, and then returns an array of
|
11
|
+
# terminals. Caches the result.
|
12
|
+
#
|
13
|
+
# @return [Array<Token::Terminal>]
|
14
|
+
def terminals
|
15
|
+
@_terminals ||= begin
|
16
|
+
@compiler.options.fetch(:terminals) { [] }.map do |v|
|
17
|
+
Token::Terminal.new(*v)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# A list of all nonterminals in the grammar.
|
23
|
+
#
|
24
|
+
# @return [Array<Symbol>]
|
25
|
+
# @see #productions
|
26
|
+
def nonterminals
|
27
|
+
@_nonterminals ||= productions.keys
|
28
|
+
end
|
29
|
+
|
30
|
+
# A list of all nonterminals, with types.
|
31
|
+
#
|
32
|
+
# @return [Array<Token::Nonterminal>>]
|
33
|
+
def typed_nonterminals
|
34
|
+
@_typed_nonterminals ||= begin
|
35
|
+
typed = []
|
36
|
+
compiler.options[:nonterminals].each do |data|
|
37
|
+
data[1].each do |nonterm|
|
38
|
+
typed << Token::Nonterminal.new(nonterm, data[0])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
typed
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# A list of all symbols in the grammar; includes both
|
46
|
+
# terminals and nonterminals.
|
47
|
+
#
|
48
|
+
# @return [Array<Token::Terminal, Symbol>]
|
49
|
+
# @see #terminals
|
50
|
+
# @see #nonterminals
|
51
|
+
def symbols
|
52
|
+
@_symbols ||= terminals + nonterminals
|
53
|
+
end
|
54
|
+
|
55
|
+
# Checks to see if the grammar uses the `error` terminal
|
56
|
+
# anywhere.
|
57
|
+
#
|
58
|
+
# @return [Boolean]
|
59
|
+
def contains_error_token?
|
60
|
+
all_productions.any? { |_| _.items.any?(&:error?) }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -1,15 +1,13 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Antelope
|
4
|
-
|
4
|
+
class Grammar
|
5
5
|
class Token
|
6
|
-
|
7
6
|
# Defines an error token. This may be used internally by the
|
8
7
|
# parser when it enters panic mode; any tokens following this
|
9
8
|
# are the synchronisation tokens. This is considered a terminal
|
10
9
|
# for the purposes of rule definitions.
|
11
10
|
class Error < Terminal
|
12
|
-
|
13
11
|
# Initialize the error token. Technically takes no arguments.
|
14
12
|
# Sets the name to be `:$error`.
|
15
13
|
def initialize(*)
|
@@ -1,21 +1,18 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
|
3
|
+
require 'antelope/grammar/token/nonterminal'
|
4
|
+
require 'antelope/grammar/token/terminal'
|
5
|
+
require 'antelope/grammar/token/epsilon'
|
6
|
+
require 'antelope/grammar/token/error'
|
8
7
|
|
9
8
|
module Antelope
|
10
|
-
|
11
|
-
|
9
|
+
class Grammar
|
12
10
|
# Defines a token type for productions/rules.
|
13
11
|
#
|
14
12
|
# @abstract This class should be inherited to define a real token.
|
15
13
|
# A base class does not match any token; however, any token can
|
16
14
|
# match the base class.
|
17
15
|
class Token
|
18
|
-
|
19
16
|
# The name of the token.
|
20
17
|
#
|
21
18
|
# @return [Symbol]
|
@@ -138,16 +135,16 @@ module Antelope
|
|
138
135
|
# @see #name
|
139
136
|
def to_s
|
140
137
|
buf = if @value
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
138
|
+
@value.inspect
|
139
|
+
else
|
140
|
+
@name.to_s
|
141
|
+
end
|
145
142
|
|
146
|
-
if from
|
147
|
-
buf <<
|
143
|
+
if from || to
|
144
|
+
buf << '('
|
148
145
|
buf << "#{from.id}" if from
|
149
146
|
buf << ":#{to.id}" if to
|
150
|
-
buf <<
|
147
|
+
buf << ')'
|
151
148
|
end
|
152
149
|
|
153
150
|
buf
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'hashie'
|
4
|
+
require 'antelope/grammar/symbols'
|
5
|
+
require 'antelope/grammar/productions'
|
6
|
+
require 'antelope/grammar/production'
|
7
|
+
require 'antelope/grammar/precedences'
|
8
|
+
require 'antelope/grammar/precedence'
|
9
|
+
require 'antelope/grammar/loading'
|
10
|
+
require 'antelope/grammar/generation'
|
11
|
+
require 'antelope/grammar/token'
|
12
|
+
|
13
|
+
module Antelope
|
14
|
+
# Defines a grammar from an Ace file. This handles setting up
|
15
|
+
# productions, loading from files, symbols, precedence, and
|
16
|
+
# generation.
|
17
|
+
class Grammar
|
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
|
data/lib/antelope/version.rb
CHANGED