antelope 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
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