antelope 0.1.8 → 0.1.9

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/CONTRIBUTING.md +4 -4
  4. data/GENERATORS.md +61 -19
  5. data/README.md +84 -9
  6. data/TODO.md +58 -0
  7. data/examples/deterministic.ace +21 -9
  8. data/examples/example.ace +16 -10
  9. data/examples/example.output +213 -146
  10. data/examples/simple.ace +1 -1
  11. data/lib/antelope/ace/compiler.rb +52 -15
  12. data/lib/antelope/ace/errors.rb +7 -0
  13. data/lib/antelope/ace/grammar/generation.rb +3 -3
  14. data/lib/antelope/ace/grammar/precedences.rb +5 -7
  15. data/lib/antelope/ace/grammar/productions.rb +36 -11
  16. data/lib/antelope/ace/grammar/{terminals.rb → symbols.rb} +25 -2
  17. data/lib/antelope/ace/grammar.rb +12 -3
  18. data/lib/antelope/ace/precedence.rb +4 -0
  19. data/lib/antelope/ace/scanner/argument.rb +57 -0
  20. data/lib/antelope/ace/scanner/first.rb +32 -6
  21. data/lib/antelope/ace/scanner/second.rb +23 -8
  22. data/lib/antelope/ace/scanner.rb +32 -26
  23. data/lib/antelope/ace/token.rb +21 -2
  24. data/lib/antelope/cli.rb +22 -2
  25. data/lib/antelope/generation/constructor/first.rb +1 -1
  26. data/lib/antelope/generation/constructor.rb +2 -0
  27. data/lib/antelope/generation/null.rb +13 -0
  28. data/lib/antelope/generation/recognizer/rule.rb +4 -3
  29. data/lib/antelope/generation/recognizer/state.rb +18 -3
  30. data/lib/antelope/generation/recognizer.rb +19 -24
  31. data/lib/antelope/generation/tableizer.rb +30 -2
  32. data/lib/antelope/generation.rb +1 -0
  33. data/lib/antelope/generator/base.rb +150 -13
  34. data/lib/antelope/generator/c.rb +11 -0
  35. data/lib/antelope/generator/c_header.rb +105 -0
  36. data/lib/antelope/generator/c_source.rb +39 -0
  37. data/lib/antelope/generator/null.rb +5 -0
  38. data/lib/antelope/generator/output.rb +3 -3
  39. data/lib/antelope/generator/ruby.rb +23 -5
  40. data/lib/antelope/generator/templates/c_header.ant +36 -0
  41. data/lib/antelope/generator/templates/c_source.ant +202 -0
  42. data/lib/antelope/generator/templates/output.ant +68 -0
  43. data/lib/antelope/generator/templates/ruby.ant +146 -0
  44. data/lib/antelope/generator.rb +15 -3
  45. data/lib/antelope/template/compiler.rb +78 -0
  46. data/lib/antelope/template/errors.rb +9 -0
  47. data/lib/antelope/template/scanner.rb +111 -0
  48. data/lib/antelope/template.rb +60 -0
  49. data/lib/antelope/version.rb +1 -1
  50. data/lib/antelope.rb +1 -0
  51. data/spec/antelope/template_spec.rb +39 -0
  52. data/subl/Ace (Ruby).JSON-tmLanguage +94 -0
  53. data/subl/Ace (Ruby).tmLanguage +153 -0
  54. metadata +21 -8
  55. data/examples/deterministic.output +0 -131
  56. data/examples/simple.output +0 -121
  57. data/lib/antelope/generator/templates/output.erb +0 -56
  58. data/lib/antelope/generator/templates/ruby.erb +0 -63
@@ -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
- def compile_file(file)
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 ϵ is the epsilon token
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
 
@@ -0,0 +1,13 @@
1
+ module Antelope
2
+ module Generation
3
+ class Null
4
+
5
+
6
+ def initialize(*)
7
+ end
8
+
9
+ def call(*)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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.each_with_index.
172
- all? { |e, i| e === other.right[i] }
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].flatten
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
- if rule.is_a? State
89
+ case rule
90
+ when State
90
91
  rule.rules.map(&:clone).each { |r| self << r }
91
- elsif rule.is_a? Rule
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 and
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.rules.each do |rule|
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
- private
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
- # Find a state that include a specific rule, or yields the rule.
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][:"$"] = [[:accept, rule.production.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, d)| 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
@@ -4,6 +4,7 @@ require "antelope/generation/errors"
4
4
  require "antelope/generation/constructor"
5
5
  require "antelope/generation/recognizer"
6
6
  require "antelope/generation/tableizer"
7
+ require "antelope/generation/null"
7
8
 
8
9
  module Antelope
9
10
 
@@ -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
- src_file = Pathname.new(source)
75
- .expand_path(self.class.source_root)
76
- src = src_file.open("r")
77
- context = instance_eval('binding')
78
- erb = ERB.new(src.read, nil, "%")
79
- erb.filename = source
80
- content = erb.result(context)
81
- content = yield content if block_given?
82
- dest_file = Pathname.new(destination)
83
- .expand_path(grammar.output)
84
- dest_file.open("w") do |f|
85
- f.write(content)
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].table
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,11 @@
1
+ module Antelope
2
+ module Generator
3
+ class C < Group
4
+ register_as "c", "C"
5
+
6
+ register_generator CHeader, "c-header"
7
+ register_generator CSource, "c-source"
8
+
9
+ end
10
+ end
11
+ 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