Class: Antelope::Ace::Compiler

Inherits:
Object
  • Object
show all
Defined in:
lib/antelope/ace/compiler.rb

Overview

Compiles a set of tokens generated by Scanner. These tokens may not nessicarily have been generated by Scanner, however the tokens must follow the same rules even still.

A list of all tokens that this compiler accepts:

  • :directive (2 arguments)
  • :copy (1 argument)
  • :second (no arguments)
  • :label (2 arguments)
  • :part (2 arguments)
  • :or (no arguments)
  • :prec (1 argument)
  • :block (1 argument)
  • :third (no arguments)
  • :body (1 argument)

The tokens are handled by methods that follow the rule compile_<token name>.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tokens) ⇒ Compiler

Initialize the compiler. The compiler keeps track of a state; this state is basically which part of the file we're in. The state can be :first, :second, or :third; some tokens may not exist in certain states.

Parameters:

  • tokens (Array<Array<(Symbol, Object, ...)>>)

    the tokens from the Scanner.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/antelope/ace/compiler.rb', line 88

def initialize(tokens)
  @tokens   = tokens
  @body     = ""
  @state    = :first
  @rules    = []
  @current  = nil
  @current_label = nil
  @options  = {
    :terminals    => [],
    :nonterminals => [],
    :prec         => [],
    :type         => nil,
    :extra        => Hashie::Extensions::IndifferentAccess.
      inject!({})
  }
end

Instance Attribute Details

#bodyString

The body of the output compiler. This should be formatted in the language that the parser is to be written in. Some output generators may have special syntax that allows the parser to be put in the body; see the output generators for more.

Returns:

  • (String)


35
36
37
# File 'lib/antelope/ace/compiler.rb', line 35

def body
  @body
end

#optionsHash

Options defined by directives in the first part of the file.

  • :terminals (Array<Symbol, String?>) — A list of all of the terminals in the language. If this is not properly defined, the grammar will throw an error saying that a symbol used in the grammar is not defined.
  • :prec (Array<(Symbol, Array<Symbol>)>) — A list of the precedence rules of the grammar. The first element of each element is the type of precedence (and should be any of :left, :right, or :nonassoc), and the second element should be the symbols that are on that level.
  • :type (String) — The type of generator to generate; this should be a language.
  • :extra (Hash<Symbol, Array<Object>>) — Extra options that are not defined here.

Returns:

  • (Hash)


70
71
72
# File 'lib/antelope/ace/compiler.rb', line 70

def options
  @options
end

#rulesArray<Hash>

A list of all the rules that are defined in the file. The rules are defined as such:

  • label (Symbol) — The left-hand side of the rule; this is the nonterminal that the right side reduces to.
  • set (Array<Symbol>) — The right-hand side of the rule. This is a combination of terminals and nonterminals.
  • block (String) — The code to be run on a reduction. this should be formatted in the language that the output parser is written in. Optional; default value is "".
  • prec (String) — The precedence level for the rule. This should be a nonterminal or terminal. Optional; default value is "".

Returns:

  • (Array<Hash>)


52
53
54
# File 'lib/antelope/ace/compiler.rb', line 52

def rules
  @rules
end

Class Method Details

.compile(tokens) ⇒ Compiler

Creates a compiler, and then runs the compiler.

Parameters:

  • tokens (Array<Array<(Symbol, Object, ...)>>)

    the tokens from the Scanner.

Returns:

See Also:



77
78
79
# File 'lib/antelope/ace/compiler.rb', line 77

def self.compile(tokens)
  new(tokens).compile
end

Instance Method Details

#compare_versions(required) ⇒ void (private)

This method returns an undefined value.

Compares the required version and the Antelope version.

Raises:



322
323
324
325
326
327
328
329
330
331
# File 'lib/antelope/ace/compiler.rb', line 322

def compare_versions(required)
  antelope_version = Gem::Version.new(Antelope::VERSION)
  required_version = Gem::Requirement.new(required)

  unless required_version =~ antelope_version
    raise IncompatibleVersionError,
      "Grammar requires #{required}, " \
      "have #{Antelope::VERSION}"
  end
end

#compileself

Runs the compiler on the input tokens. For each token, it calls compile_<type> with <type> being the first element of the token, with the remaining part of the array passed as arguments.

Returns:

  • (self)


118
119
120
121
122
123
124
# File 'lib/antelope/ace/compiler.rb', line 118

def compile
  @tokens.each do |token|
    send(:"compile_#{token[0]}", *token[1..-1])
  end

  self
end

#compile_block(block) ⇒ void

This method returns an undefined value.

Compiles a block. This should only occur in a rule definition, and in the second part. It sets the block on the current rule.

Parameters:

  • block (String)

    the block.



273
274
275
276
# File 'lib/antelope/ace/compiler.rb', line 273

def compile_block(block)
  require_state! :second
  @current[:block] = block
end

#compile_copy(body) ⇒ void

This method returns an undefined value.

Compiles a copy token. A copy token basically copies its argument directly into the body. Used in both the first and third parts.

Parameters:

  • body (String)

    the string to copy into the body.



197
198
199
200
# File 'lib/antelope/ace/compiler.rb', line 197

def compile_copy(body)
  require_state! :first, :third
  @body << body
end

#compile_directive(name, args) ⇒ void

This method returns an undefined value.

Compiles a directive. This may only be triggered in the first section of the file. The directive accepts two arguments. The directive name can be any of the following:

  • :terminal — adds a terminal. Requires 1-2 arguments; the first argument is the terminal name, and the second argument is a string that can represent the terminal.
  • :require — requires a certain version of Antelope. Requires 1 argument. If the first argument is a version greater than the current version of Antelope, it raises an error.
  • :left — creates a new precedence level, with the argument values being the symbols. The precedence level is left associative.
  • :right — creates a new precedence level, with the argument valeus being the symbols. The precedence level is right associative.
  • :nonassoc — creates a nre precedence level, with the argument values being the symbols. The precedence level is nonassociative.
  • :type — the type of parser to generate. This should correspond to the output language of the parser.

Parameters:

  • name (String, Symbol)

    the name of the directive. Accepts any of :terminal, :require, :left, :right, :nonassoc, and :type. Any other values produce an error on stderr and are put in the :extra hash on #options.

  • args (Array<String>)

    the arguments to the directive.

See Also:



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/antelope/ace/compiler.rb', line 157

def compile_directive(name, args)
  require_state! :first
  name = name.intern
  case name
  when :terminal, :token
    handle_token(args)
  when :require
    compare_versions(args[0])
  when :left, :right, :nonassoc
    options[:prec] << [name, *args.map(&:intern)]
  when :language, :generator, :"grammar.type"
    options[:type] = args[0].downcase
  when :type
    raise SyntaxError, "%type directive requires first " \
      "argument to be caret" unless args[0].caret?

    options[:nonterminals] <<
      [args[0], args[1..-1].map(&:intern)]
  when :define
    compile_extra(args[0], args[1..-1])
  else
    compile_extra(name, args)
  end
end

#compile_extra(name, args) ⇒ Object

Raises:



182
183
184
185
186
187
188
189
# File 'lib/antelope/ace/compiler.rb', line 182

def compile_extra(name, args)
  matching = Generator.directives[name.to_s]

  raise NoDirectiveError, "no directive named #{name}" \
    unless matching

  options[:extra][name] = args
end

#compile_label(label, val) ⇒ void

This method returns an undefined value.

Compiles a label. This starts a rule definition. The token should only exist in the second part. A rule definition occurs by setting the @current_label to the first argument, and @current to a blank rule save the label set. If a rule definition was already in progress, it is completed.

Parameters:

  • label (String)

    the left-hand side of the rule; it should be a nonterminal.



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/antelope/ace/compiler.rb', line 218

def compile_label(label, val)
  require_state! :second
  if @current
    @rules << @current
  end

  label = label.intern
  @current_label = [label, val]

  @current = {
    label: label,
    label_id: val,
    set:   [],
    block: "",
    prec:  ""
  }
end

#compile_orvoid

This method returns an undefined value.

Compiles an or. This should only occur in a rule definition, and in the second part. It starts a new rule definition by calling #compile_label with the current label.

See Also:



252
253
254
# File 'lib/antelope/ace/compiler.rb', line 252

def compile_or
  compile_label(*@current_label)
end

#compile_part(text, val) ⇒ Object

Compiles a part. This should only occur during a rule definition. The token should only exist in the second part. It adds the first argument to the set of the current rule.

Parameters:

  • text (String)

    the symbol to append to the current rule.



241
242
243
244
# File 'lib/antelope/ace/compiler.rb', line 241

def compile_part(text, val)
  require_state! :second
  @current[:set] << [text.intern, val]
end

#compile_prec(prec) ⇒ void

This method returns an undefined value.

Compiles the precedence operator. This should only occur in a rule definition, and in the second part. It sets the precedence definition on the current rule.

Parameters:

  • prec (String)

    the precedence of the rule.



262
263
264
265
# File 'lib/antelope/ace/compiler.rb', line 262

def compile_prec(prec)
  require_state! :second
  @current[:prec] = prec
end

#compile_secondvoid

This method returns an undefined value.

Sets the state to the second part.



205
206
207
# File 'lib/antelope/ace/compiler.rb', line 205

def compile_second
  @state = :second
end

#compile_thirdvoid

This method returns an undefined value.

Sets the state to the third part. If a rule definition was in progress, it finishes the rule.



282
283
284
285
286
287
288
289
# File 'lib/antelope/ace/compiler.rb', line 282

def compile_third
  if @current
    @rules << @current
    @current_label = @current = nil
  end

  @state = :third
end

#handle_token(args) ⇒ Object (private)



293
294
295
296
297
298
299
300
301
302
303
# File 'lib/antelope/ace/compiler.rb', line 293

def handle_token(args)
  type = ""
  if args[0].caret?
    type = args.shift
  end

  name = args.shift
  value = args.shift

  options[:terminals] << [name.intern, type, nil, value]
end

#require_state!(*state) ⇒ void (private)

This method returns an undefined value.

Checks the current state against the given states.

Raises:



310
311
312
313
314
315
# File 'lib/antelope/ace/compiler.rb', line 310

def require_state!(*state)
  raise InvalidStateError,
    "In state #{@state}, " \
    "required state #{state.join(", ")}" \
    unless state.include?(@state)
end