antelope 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +2 -0
- data/CONTRIBUTING.md +4 -4
- data/GENERATORS.md +61 -19
- data/README.md +84 -9
- data/TODO.md +58 -0
- data/examples/deterministic.ace +21 -9
- data/examples/example.ace +16 -10
- data/examples/example.output +213 -146
- data/examples/simple.ace +1 -1
- data/lib/antelope/ace/compiler.rb +52 -15
- data/lib/antelope/ace/errors.rb +7 -0
- data/lib/antelope/ace/grammar/generation.rb +3 -3
- data/lib/antelope/ace/grammar/precedences.rb +5 -7
- data/lib/antelope/ace/grammar/productions.rb +36 -11
- data/lib/antelope/ace/grammar/{terminals.rb → symbols.rb} +25 -2
- data/lib/antelope/ace/grammar.rb +12 -3
- data/lib/antelope/ace/precedence.rb +4 -0
- data/lib/antelope/ace/scanner/argument.rb +57 -0
- data/lib/antelope/ace/scanner/first.rb +32 -6
- data/lib/antelope/ace/scanner/second.rb +23 -8
- data/lib/antelope/ace/scanner.rb +32 -26
- data/lib/antelope/ace/token.rb +21 -2
- data/lib/antelope/cli.rb +22 -2
- data/lib/antelope/generation/constructor/first.rb +1 -1
- data/lib/antelope/generation/constructor.rb +2 -0
- data/lib/antelope/generation/null.rb +13 -0
- data/lib/antelope/generation/recognizer/rule.rb +4 -3
- data/lib/antelope/generation/recognizer/state.rb +18 -3
- data/lib/antelope/generation/recognizer.rb +19 -24
- data/lib/antelope/generation/tableizer.rb +30 -2
- data/lib/antelope/generation.rb +1 -0
- data/lib/antelope/generator/base.rb +150 -13
- data/lib/antelope/generator/c.rb +11 -0
- data/lib/antelope/generator/c_header.rb +105 -0
- data/lib/antelope/generator/c_source.rb +39 -0
- data/lib/antelope/generator/null.rb +5 -0
- data/lib/antelope/generator/output.rb +3 -3
- data/lib/antelope/generator/ruby.rb +23 -5
- data/lib/antelope/generator/templates/c_header.ant +36 -0
- data/lib/antelope/generator/templates/c_source.ant +202 -0
- data/lib/antelope/generator/templates/output.ant +68 -0
- data/lib/antelope/generator/templates/ruby.ant +146 -0
- data/lib/antelope/generator.rb +15 -3
- data/lib/antelope/template/compiler.rb +78 -0
- data/lib/antelope/template/errors.rb +9 -0
- data/lib/antelope/template/scanner.rb +111 -0
- data/lib/antelope/template.rb +60 -0
- data/lib/antelope/version.rb +1 -1
- data/lib/antelope.rb +1 -0
- data/spec/antelope/template_spec.rb +39 -0
- data/subl/Ace (Ruby).JSON-tmLanguage +94 -0
- data/subl/Ace (Ruby).tmLanguage +153 -0
- metadata +21 -8
- data/examples/deterministic.output +0 -131
- data/examples/simple.output +0 -121
- data/lib/antelope/generator/templates/output.erb +0 -56
- data/lib/antelope/generator/templates/ruby.erb +0 -63
data/lib/antelope/ace/token.rb
CHANGED
@@ -35,14 +35,30 @@ module Antelope
|
|
35
35
|
# @return [Recognizer::State]
|
36
36
|
attr_accessor :to
|
37
37
|
|
38
|
+
# The type of the token. This is given by a caret argument to
|
39
|
+
# the grammar. This is primarily used for generators.
|
40
|
+
#
|
41
|
+
# @return [String]
|
42
|
+
attr_accessor :type
|
43
|
+
|
44
|
+
attr_accessor :id
|
45
|
+
|
38
46
|
# Initialize.
|
39
47
|
#
|
40
48
|
# @param name [Symbol] the name of the token.
|
49
|
+
# @param type [String?] the type of the token. For definitions,
|
50
|
+
# this is the given type of the token (for typed language
|
51
|
+
# output).
|
52
|
+
# @param id [String?] the id of the token in the production.
|
53
|
+
# For some languages, this allows references to the token via
|
54
|
+
# the id.
|
41
55
|
# @param value [String?] the value of the token. This is only
|
42
56
|
# used in output representation to the developer.
|
43
|
-
def initialize(name, value = nil)
|
57
|
+
def initialize(name, type = nil, id = nil, value = nil)
|
44
58
|
@name = name
|
45
59
|
@value = value
|
60
|
+
@type = type
|
61
|
+
@id = id
|
46
62
|
@from = nil
|
47
63
|
@to = nil
|
48
64
|
end
|
@@ -110,6 +126,9 @@ module Antelope
|
|
110
126
|
buf
|
111
127
|
end
|
112
128
|
|
129
|
+
# Returns a nice inspect.
|
130
|
+
#
|
131
|
+
# @return [String]
|
113
132
|
def inspect
|
114
133
|
"#<#{self.class} from=#{from.id if from} to=#{to.id if to} " \
|
115
134
|
"name=#{name.inspect} value=#{@value.inspect}>"
|
@@ -149,7 +168,7 @@ module Antelope
|
|
149
168
|
#
|
150
169
|
# @return [Token]
|
151
170
|
def without_transitions
|
152
|
-
self.class.new(name, @value)
|
171
|
+
self.class.new(name, @type, @id, @value)
|
153
172
|
end
|
154
173
|
|
155
174
|
# Generates a hash for this class.
|
data/lib/antelope/cli.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require "thor"
|
4
4
|
|
5
5
|
module Antelope
|
6
|
+
|
7
|
+
# Handles the command line interface.
|
6
8
|
class CLI < Thor
|
7
9
|
|
8
10
|
class_option :verbose, default: false, type: :boolean
|
@@ -10,19 +12,37 @@ module Antelope
|
|
10
12
|
option :type, default: nil, type: :string,
|
11
13
|
desc: "The type of generator to use"
|
12
14
|
desc "compile FILE [FILE]*", "compile the given files"
|
15
|
+
|
16
|
+
# Compile.
|
13
17
|
def compile(*files)
|
14
18
|
files.each do |file|
|
15
19
|
compile_file(file)
|
16
20
|
end
|
17
21
|
end
|
18
22
|
|
23
|
+
desc "check FILE [FILE]*", "check the syntax of the given files"
|
24
|
+
|
25
|
+
# Check.
|
26
|
+
def check(*files)
|
27
|
+
files.each do |file|
|
28
|
+
compile_file(file, [Generator::Null])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
19
32
|
private
|
20
33
|
|
21
|
-
|
34
|
+
# Compiles the given file, and then generates. If an error
|
35
|
+
# occurs, it prints it out to stderr, along with a backtrace if
|
36
|
+
# the verbose flag was set.
|
37
|
+
#
|
38
|
+
# @param file [String] the file to compile.
|
39
|
+
# @param gen [Array, Symbol] the generator to use.
|
40
|
+
# @return [void]
|
41
|
+
def compile_file(file, gen = :guess)
|
22
42
|
puts "Compiling #{file}... "
|
23
43
|
|
24
44
|
grammar = Ace::Grammar.from_file(file)
|
25
|
-
grammar.generate(options)
|
45
|
+
grammar.generate(options, gen)
|
26
46
|
|
27
47
|
rescue => e
|
28
48
|
$stderr.puts "Error while compiling: #{e.class}: #{e.message}"
|
@@ -16,7 +16,7 @@ module Antelope
|
|
16
16
|
# Constructs the first set for a given token. This is how
|
17
17
|
# the method should behave:
|
18
18
|
#
|
19
|
-
# FIRST(ε) == [] # if
|
19
|
+
# FIRST(ε) == [] # if ε is the epsilon token
|
20
20
|
# FIRST(x) == [x] # if x is a terminal
|
21
21
|
# FIRST(αβ) == if nullable?(α)
|
22
22
|
# FIRST(α) U FIRST(β)
|
@@ -63,6 +63,7 @@ module Antelope
|
|
63
63
|
production.items = []
|
64
64
|
|
65
65
|
current_state = state
|
66
|
+
old_state = state
|
66
67
|
|
67
68
|
production.label.from = state
|
68
69
|
production.label.to = state.transitions[rule.left.name]
|
@@ -78,6 +79,7 @@ module Antelope
|
|
78
79
|
|
79
80
|
production.items << new_item
|
80
81
|
|
82
|
+
old_state = current_state
|
81
83
|
current_state = transition
|
82
84
|
end
|
83
85
|
|
@@ -168,8 +168,9 @@ module Antelope
|
|
168
168
|
# @return [Numeric]
|
169
169
|
def ===(other)
|
170
170
|
if other.is_a? Rule
|
171
|
-
left === other.left and right.
|
172
|
-
|
171
|
+
left === other.left and right.size == other.right.size and
|
172
|
+
right.each_with_index.
|
173
|
+
all? { |e, i| e === other.right[i] }
|
173
174
|
else
|
174
175
|
super
|
175
176
|
end
|
@@ -203,7 +204,7 @@ module Antelope
|
|
203
204
|
# @private
|
204
205
|
# @return [Array<(Ace::Token::Nonterminal, Array<Ace::Token>, Numeric)>]
|
205
206
|
def to_a
|
206
|
-
[left, right, position]
|
207
|
+
[left, right, position]
|
207
208
|
end
|
208
209
|
end
|
209
210
|
end
|
@@ -86,10 +86,15 @@ module Antelope
|
|
86
86
|
# @param rule [State, Rule] the object to append.
|
87
87
|
# @return [self]
|
88
88
|
def <<(rule)
|
89
|
-
|
89
|
+
case rule
|
90
|
+
when State
|
90
91
|
rule.rules.map(&:clone).each { |r| self << r }
|
91
|
-
|
92
|
+
when Rule
|
92
93
|
rules << rule unless rules.include? rule
|
94
|
+
when Array, Set
|
95
|
+
rule.each do |part|
|
96
|
+
self << part
|
97
|
+
end
|
93
98
|
else
|
94
99
|
raise ArgumentError, "Expected #{State} or #{Rule}, " \
|
95
100
|
"got #{rule.class}"
|
@@ -100,11 +105,21 @@ module Antelope
|
|
100
105
|
|
101
106
|
alias_method :push, :<<
|
102
107
|
|
108
|
+
# Check to see if this state is fuzzily equivalent to another
|
109
|
+
# state. It does this by checking if the transitions are
|
110
|
+
# equivalent, and then that the rules are fuzzily equivalent.
|
111
|
+
# Ideally, the method is commutative; that is,
|
112
|
+
# `(a === b) == (b === a)`.
|
113
|
+
#
|
114
|
+
# @param other [State] the state to check.
|
115
|
+
# @return [Boolean]
|
116
|
+
# @see Rule#===
|
103
117
|
def ===(other)
|
104
118
|
return super unless other.is_a? State
|
105
119
|
|
106
120
|
other_rules = other.rules.to_a
|
107
|
-
other.transitions == transitions
|
121
|
+
other.transitions == transitions &&
|
122
|
+
rules.size == other_rules.size &&
|
108
123
|
rules.each_with_index.
|
109
124
|
all? { |rule, i| rule === other_rules[i] }
|
110
125
|
end
|
@@ -90,21 +90,7 @@ module Antelope
|
|
90
90
|
def compute_states
|
91
91
|
fixed_point(states) do
|
92
92
|
states.dup.each do |state|
|
93
|
-
state
|
94
|
-
next unless rule.succ?
|
95
|
-
transitional = find_state_for(rule.succ) do |succ|
|
96
|
-
ns = State.new << succ
|
97
|
-
compute_closure(ns)
|
98
|
-
@map[succ] = ns
|
99
|
-
end
|
100
|
-
|
101
|
-
if state.transitions[rule.active.name]
|
102
|
-
state.transitions[rule.active.name].merge! transitional
|
103
|
-
else
|
104
|
-
states << transitional
|
105
|
-
state.transitions[rule.active.name] = transitional
|
106
|
-
end
|
107
|
-
end
|
93
|
+
compute_gotos(state)
|
108
94
|
end
|
109
95
|
end
|
110
96
|
end
|
@@ -124,18 +110,27 @@ module Antelope
|
|
124
110
|
end
|
125
111
|
end
|
126
112
|
|
127
|
-
|
113
|
+
def compute_gotos(state)
|
114
|
+
actives = state.rules.map(&:active).select(&:name).to_set
|
115
|
+
|
116
|
+
actives.each do |active|
|
117
|
+
next if state.transitions[active.name]
|
118
|
+
rules = state.rules.
|
119
|
+
select { |r| r.active == active && r.succ? }.
|
120
|
+
map(&:succ).to_set
|
121
|
+
s = states.find { |st| rules <= st.rules } || begin
|
122
|
+
s = State.new << rules
|
123
|
+
compute_closure(s)
|
124
|
+
states << s
|
125
|
+
s
|
126
|
+
end
|
128
127
|
|
129
|
-
|
130
|
-
|
131
|
-
# @param rule [Rule]
|
132
|
-
# @yield [rule]
|
133
|
-
# @return [State]
|
134
|
-
def find_state_for(rule, &block)
|
135
|
-
#states.find { |state| state.include?(rule) } or yield(rule)
|
136
|
-
@map.fetch(rule) { block.call(rule) }
|
128
|
+
state.transitions[active.name] = s
|
129
|
+
end
|
137
130
|
end
|
138
131
|
|
132
|
+
private
|
133
|
+
|
139
134
|
# Changes the IDs of the states into a more friendly format.
|
140
135
|
#
|
141
136
|
# @return [void]
|
@@ -38,6 +38,7 @@ module Antelope
|
|
38
38
|
def call
|
39
39
|
tablize
|
40
40
|
conflictize
|
41
|
+
defaultize
|
41
42
|
end
|
42
43
|
|
43
44
|
# Construct a table based on the grammar. The table itself is
|
@@ -67,7 +68,8 @@ module Antelope
|
|
67
68
|
end
|
68
69
|
|
69
70
|
if rule.production.id.zero?
|
70
|
-
table[state.id][
|
71
|
+
table[state.id][:$end] =
|
72
|
+
[[:accept, rule.production.id]]
|
71
73
|
end
|
72
74
|
end
|
73
75
|
end
|
@@ -94,7 +96,7 @@ module Antelope
|
|
94
96
|
|
95
97
|
terminal = grammar.precedence_for(on)
|
96
98
|
|
97
|
-
rule_part, other_part = data.sort_by { |(t,
|
99
|
+
rule_part, other_part = data.sort_by { |(t, _)| t }
|
98
100
|
|
99
101
|
unless other_part[0] == :state
|
100
102
|
$stderr.puts \
|
@@ -121,6 +123,32 @@ module Antelope
|
|
121
123
|
end
|
122
124
|
end
|
123
125
|
end
|
126
|
+
|
127
|
+
# Reduce many transitions into a single `$default` transition.
|
128
|
+
# This only works if there is no `$empty` transition; if there
|
129
|
+
# is an `$empty` transition, then the `$default` transition is
|
130
|
+
# set to be the `$empty` transition.
|
131
|
+
#
|
132
|
+
# @return [void]
|
133
|
+
def defaultize
|
134
|
+
max = @table.map { |s| s.keys.size }.max
|
135
|
+
@table.each_with_index do |state|
|
136
|
+
if state.key?(:$empty)
|
137
|
+
state[:$default] = state[:$empty]
|
138
|
+
else
|
139
|
+
common = state.group_by { |k, v| v }.values.
|
140
|
+
sort_by(&:size).first
|
141
|
+
|
142
|
+
if common.size > (max / 2)
|
143
|
+
action = common[0][1]
|
144
|
+
|
145
|
+
keys = common.map(&:first)
|
146
|
+
state.delete_if { |k, _| keys.include?(k) }
|
147
|
+
state[:$default] = action
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
124
152
|
end
|
125
153
|
end
|
126
154
|
end
|
data/lib/antelope/generation.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'hashie/mash'
|
2
|
+
|
1
3
|
module Antelope
|
2
4
|
module Generator
|
3
5
|
|
@@ -10,6 +12,7 @@ module Antelope
|
|
10
12
|
# @abstract Subclass and redefine {#generate} to create a
|
11
13
|
# generator.
|
12
14
|
class Base
|
15
|
+
Boolean = Object.new
|
13
16
|
# The modifiers that were applied to the grammar.
|
14
17
|
#
|
15
18
|
# @return [Hash<(Symbol, Object)>]
|
@@ -37,6 +40,43 @@ module Antelope
|
|
37
40
|
Generator.register_generator(self, *names)
|
38
41
|
end
|
39
42
|
|
43
|
+
# Called by ruby on subclassing.
|
44
|
+
#
|
45
|
+
# @param subclass [Class]
|
46
|
+
# @return [void]
|
47
|
+
def self.inherited(subclass)
|
48
|
+
directives.each do |name, (_, type)|
|
49
|
+
subclass.has_directive(name, type)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Allows a directive for this generator. This is checked in
|
54
|
+
# the compiler to allow the option. If the compiler encounters
|
55
|
+
# a bad directive, it'll error (to give the developer a warning).
|
56
|
+
#
|
57
|
+
# @param directive [Symbol, String]
|
58
|
+
# @param type [Object] used to define how the value should be
|
59
|
+
# coerced.
|
60
|
+
# @see #directives
|
61
|
+
# @see #coerce_directive_value
|
62
|
+
# @return [void]
|
63
|
+
def self.has_directive(directive, type = nil)
|
64
|
+
directive = directive.to_s
|
65
|
+
directives[directive] = [self, type]
|
66
|
+
end
|
67
|
+
|
68
|
+
# The directives in the class.
|
69
|
+
#
|
70
|
+
# @see .has_directive
|
71
|
+
# @return [Hash]
|
72
|
+
def self.directives
|
73
|
+
@_directives ||= {}
|
74
|
+
end
|
75
|
+
|
76
|
+
class << self
|
77
|
+
alias_method :has_directives, :has_directive
|
78
|
+
end
|
79
|
+
|
40
80
|
# Initialize the generator.
|
41
81
|
#
|
42
82
|
# @param grammar [Grammar]
|
@@ -57,6 +97,93 @@ module Antelope
|
|
57
97
|
|
58
98
|
protected
|
59
99
|
|
100
|
+
# Retrieves all directives from the grammar, and giving them the
|
101
|
+
# proper values for this instance.
|
102
|
+
#
|
103
|
+
# @see .has_directive
|
104
|
+
# @see #coerce_directive_value
|
105
|
+
# @return [Hash]
|
106
|
+
def directives
|
107
|
+
@_directives ||= begin
|
108
|
+
hash = Hashie::Mash.new
|
109
|
+
|
110
|
+
self.class.directives.each do |key, dict|
|
111
|
+
value = [grammar.options.key?(key), grammar.options[key]]
|
112
|
+
hash.deep_merge! coerce_nested_hash(key,
|
113
|
+
coerce_directive_value(*value, dict[1]))
|
114
|
+
end
|
115
|
+
|
116
|
+
hash
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def coerce_nested_hash(key, value)
|
121
|
+
parts = key.split('.').map { |p| p.gsub(/-/, "_") }
|
122
|
+
top = {}
|
123
|
+
hash = top
|
124
|
+
parts.each_with_index do |part, i|
|
125
|
+
hash[part] = if parts.last == part
|
126
|
+
value
|
127
|
+
else
|
128
|
+
{}
|
129
|
+
end
|
130
|
+
hash = hash[part]
|
131
|
+
end
|
132
|
+
|
133
|
+
top[key] = value
|
134
|
+
top
|
135
|
+
end
|
136
|
+
|
137
|
+
# Coerce the given directive value to the given type. For the
|
138
|
+
# type `nil`, it checks the size of the values; for no values,
|
139
|
+
# it returns true; for one value, it returns that one value; for
|
140
|
+
# any other size value, it returns the values. For the type
|
141
|
+
# `Boolean`, if no values were given, or if the first value isn't
|
142
|
+
# "false", it returns true. For the type `:single` (or `:one`),
|
143
|
+
# it returns the first value. For the type `Array`, it returns
|
144
|
+
# the values. For any other type that is a class, it tries to
|
145
|
+
# initialize the class with the given arguments.
|
146
|
+
def coerce_directive_value(defined, values, type)
|
147
|
+
return nil unless defined || Array === type
|
148
|
+
case type
|
149
|
+
when nil
|
150
|
+
case values.size
|
151
|
+
when 0
|
152
|
+
true
|
153
|
+
when 1
|
154
|
+
values[0]
|
155
|
+
else
|
156
|
+
values
|
157
|
+
end
|
158
|
+
when :single, :one
|
159
|
+
values[0]
|
160
|
+
when Boolean
|
161
|
+
# For bool, if there were no arguments, then return true;
|
162
|
+
# otherwise, if the first argument isn't "false", return
|
163
|
+
# true.
|
164
|
+
|
165
|
+
values[0].to_s != "false"
|
166
|
+
when Array
|
167
|
+
values.zip(type).map do |value, t|
|
168
|
+
coerce_directive_value(defined, [value], t)
|
169
|
+
end
|
170
|
+
when Class
|
171
|
+
if type == Array
|
172
|
+
values
|
173
|
+
elsif type == String
|
174
|
+
values[0].to_s
|
175
|
+
elsif [Fixnum, Integer, Numeric].include?(type)
|
176
|
+
values[0].to_i
|
177
|
+
elsif type == Float
|
178
|
+
values[0].to_f
|
179
|
+
else
|
180
|
+
type.new(*values)
|
181
|
+
end
|
182
|
+
else
|
183
|
+
raise UnknownTypeError, "unknown type #{type}"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
60
187
|
# Copies a template from the source, runs it through erb (in the
|
61
188
|
# context of this class), and then outputs it at the destination.
|
62
189
|
# If given a block, it will call the block after the template is
|
@@ -71,18 +198,22 @@ module Antelope
|
|
71
198
|
# @yieldreturn [String] The new content to write to the output.
|
72
199
|
# @return [void]
|
73
200
|
def template(source, destination)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
201
|
+
src = Pathname.new("#{source}.ant").
|
202
|
+
expand_path(self.class.source_root)
|
203
|
+
|
204
|
+
template = Template.new(src)
|
205
|
+
content = template.result(instance_eval('binding'))
|
206
|
+
content.gsub!(/[ \t]+\n/, "\n")
|
207
|
+
|
208
|
+
if block_given?
|
209
|
+
content = yield content
|
210
|
+
end
|
211
|
+
|
212
|
+
dest = Pathname.new(destination).
|
213
|
+
expand_path(grammar.output)
|
214
|
+
|
215
|
+
dest.open("w") do |file|
|
216
|
+
file.write(content)
|
86
217
|
end
|
87
218
|
end
|
88
219
|
|
@@ -99,7 +230,11 @@ module Antelope
|
|
99
230
|
#
|
100
231
|
# @return [Array<Hash<Symbol => Array<(Symbol, Numeric)>>>]
|
101
232
|
def table
|
102
|
-
mods[:tableizer].
|
233
|
+
if mods[:tableizer].is_a? Generation::Tableizer
|
234
|
+
mods[:tableizer].table
|
235
|
+
else
|
236
|
+
[]
|
237
|
+
end
|
103
238
|
end
|
104
239
|
|
105
240
|
# Returns an array of the production information of each
|
@@ -122,6 +257,8 @@ module Antelope
|
|
122
257
|
production[:block]]
|
123
258
|
end
|
124
259
|
end
|
260
|
+
|
261
|
+
|
125
262
|
end
|
126
263
|
end
|
127
264
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Antelope
|
2
|
+
module Generator
|
3
|
+
class CHeader < Base
|
4
|
+
|
5
|
+
register_as "c-header", "c_header"
|
6
|
+
|
7
|
+
has_directive "union", Array[String, String]
|
8
|
+
has_directive "api.prefix", String
|
9
|
+
has_directive "api.push-pull", String
|
10
|
+
has_directive "api.value.type", String
|
11
|
+
has_directive "api.token.prefix", String
|
12
|
+
has_directive "parse-param", Array
|
13
|
+
has_directive "lex-param", Array
|
14
|
+
has_directive "param", Array
|
15
|
+
|
16
|
+
def push?
|
17
|
+
directives.api.push_pull == "push"
|
18
|
+
end
|
19
|
+
|
20
|
+
def define_stype?
|
21
|
+
!!directives.union[0] && !directives.api.value.type
|
22
|
+
end
|
23
|
+
|
24
|
+
def lex_params
|
25
|
+
params = [directives.lex_param, directives.param].compact.
|
26
|
+
flatten
|
27
|
+
|
28
|
+
if params.any?
|
29
|
+
", " << params.join(", ")
|
30
|
+
else
|
31
|
+
""
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse_params
|
36
|
+
[directives.parse_param, directives.param].compact.flatten.
|
37
|
+
join(", ")
|
38
|
+
end
|
39
|
+
|
40
|
+
def params
|
41
|
+
if directives.param
|
42
|
+
directives.param.join(", ")
|
43
|
+
else
|
44
|
+
""
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def stype
|
49
|
+
prefix.upcase << if directives.api.value.type
|
50
|
+
directives.api.value.type
|
51
|
+
elsif directives.union.size > 1
|
52
|
+
directives.union[0]
|
53
|
+
else
|
54
|
+
"STYPE"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def union_body
|
59
|
+
directives.union.last
|
60
|
+
end
|
61
|
+
|
62
|
+
def terminal_type
|
63
|
+
"int" # for now
|
64
|
+
end
|
65
|
+
|
66
|
+
def token_prefix
|
67
|
+
if directives.api.token.prefix
|
68
|
+
directives.api.token.prefix
|
69
|
+
elsif directives.api.prefix
|
70
|
+
prefix.upcase
|
71
|
+
else
|
72
|
+
""
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def prefix
|
77
|
+
if directives.api.prefix
|
78
|
+
directives.api.prefix
|
79
|
+
else
|
80
|
+
"yy"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def upper_prefix
|
85
|
+
prefix.upcase
|
86
|
+
end
|
87
|
+
|
88
|
+
def symbols
|
89
|
+
@_symbols ||= begin
|
90
|
+
sym = grammar.terminals.map(&:name) + grammar.nonterminals
|
91
|
+
nums = sym.each_with_index.map { |v, i| [v, i + 257] }
|
92
|
+
Hash[nums]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def guard_name
|
97
|
+
"#{prefix.upcase}#{file.gsub(/[\W]/, "_").upcase}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def generate
|
101
|
+
template "c_header", "#{file}.h"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Antelope
|
2
|
+
module Generator
|
3
|
+
class CSource < CHeader
|
4
|
+
|
5
|
+
def action_for(state)
|
6
|
+
out = ""
|
7
|
+
|
8
|
+
grammar.terminals.each do |terminal|
|
9
|
+
action = state[terminal.name]
|
10
|
+
|
11
|
+
if action.size == 2 && action[0] == :state
|
12
|
+
out << "#{action[1] + 1}, "
|
13
|
+
elsif action.size == 2 &&
|
14
|
+
[:reduce, :accept].include?(action[0])
|
15
|
+
if $DEBUG
|
16
|
+
out << "#{prefix.upcase}STATES + #{action[1] + 1}, "
|
17
|
+
else
|
18
|
+
out << "#{table.size + action[1] + 1}, "
|
19
|
+
end
|
20
|
+
else
|
21
|
+
out << "0, "
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
out.chomp(", ")
|
27
|
+
end
|
28
|
+
|
29
|
+
def cify_block(block)
|
30
|
+
block.gsub(/\$([0-9]+)/, "#{prefix}vals[\\1]")
|
31
|
+
.gsub(/\$\$/, "#{prefix}out")
|
32
|
+
end
|
33
|
+
|
34
|
+
def generate
|
35
|
+
template "c_source", "#{file}.c"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|