Module: Antelope::Grammar::Productions

Included in:
Antelope::Grammar
Defined in:
lib/antelope/grammar/productions.rb

Overview

Manages the productions of the grammar.

Instance Method Summary collapse

Instance Method Details

#all_productionsArray<Production>

Returns all productions for all nonterminals, sorted by id.

Returns:



20
21
22
# File 'lib/antelope/grammar/productions.rb', line 20

def all_productions
  productions.values.flatten.sort_by(&:id)
end

#default_productionProduction (private)

Creates the default production for the grammar. The left hand side of the production is the :$start symbol, with the right hand side being the first rule's left-hand side and the terminal $. This production is automagically given the last precedence, and an id of 0.

Returns:



146
147
148
149
150
151
# File 'lib/antelope/grammar/productions.rb', line 146

def default_production
  Production.new(Token::Nonterminal.new(:$start), [
      Token::Nonterminal.new(@compiler.rules.first[:label]),
      Token::Terminal.new(:$end)
    ], "", precedence.last, 0)
end

#find_token(value) ⇒ Token

Finds a token based on its corresponding symbol. First checks the productions, to see if it's a nonterminal; then, tries to find it in the terminals; otherwise, if the symbol is error, it returns a Token::Error; if the symbol is nothing or ε, it returns a Token::Epsilon; if it's none of those, it raises an UndefinedTokenError.

Parameters:

  • value (String, Symbol, #intern)

    the token's symbol to check.

Returns:

Raises:



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/antelope/grammar/productions.rb', line 35

def find_token(value)
  value = value.intern

  if productions.key?(value)
    typed_nonterminals.find { |term| term.name == value } ||
      Token::Nonterminal.new(value)
  elsif terminal = terminals.
      find { |term| term.name == value }
    terminal
  elsif value == :$error || value == :error
    Token::Error.new
  elsif [:nothing, , :"%empty"].include?(value)
    Token::Epsilon.new
  else
    raise UndefinedTokenError, "Could not find a token " \
      "named #{value.inspect}"
  end
end

#generate_production_for(rule, id) ⇒ Production (private)

Generates a production for a given compiler rule. Converts the tokens in the set to their Token counterparts, and then sets the precedence for the production. If the precedence declaration from the compiler rule is empty, then it'll use the last terminal from the set to check for precedence; otherwise, it'll use the precedence declaration. This is to make sure that every production has a precedence declaration.

Parameters:

  • rule (Hash)

    the compiler's rule.

  • id (Numeric)

    the id for the production.

Returns:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/antelope/grammar/productions.rb', line 97

def generate_production_for(rule, id)
  left  = Token::Nonterminal.new(rule[:label])
  items = rule[:set].map { |_| find_token(_[0]) }
  prec  = if rule[:prec].empty?
    items.select(&:terminal?).first
  else
    rule[:prec].intern
  end

  prec  = precedence_for(prec)
  left.type = type_for(rule[:label])
  left.id = rule[:label_id]

  rule[:set].each_with_index do |tok, i|
    items[i] = items[i].dup
    items[i].id = tok[1]
  end
  items.delete_if(&:epsilon?)

  Production.new(left, items, rule[:block], prec, id + 1)
end

#generate_productionsHash<(Symbol, Array<Production>)> (private)

Actually generates the productions. Uses the rules from the compiler to construct the productions. Makes two loops over the compiler's rules; the first to tell the grammar that the nonterminal does exist, and the second to actually construct the productions. The first loop is for #find_token, because otherwise it wouldn't be able to return a nonterminal properly.

Returns:



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/antelope/grammar/productions.rb', line 65

def generate_productions
  @_productions = {}
  index = 0

  rules = @compiler.rules.each do |rule|
    productions[rule[:label]] = []
  end

  while index < rules.size
    rule = rules[index]
    productions[rule[:label]] <<
      generate_production_for(rule, index)
    index += 1
  end

  productions[:$start] = [default_production]

  productions
end

#productionsHash<(Symbol, Array<Production>)>

Returns a hash of all of the productions. The result is cached.

Returns:



13
14
15
# File 'lib/antelope/grammar/productions.rb', line 13

def productions
  @_productions || generate_productions
end

#type_for(token) ⇒ Object (private)

Returns the defined type for the given token name. Uses the %type directive to infer the corresponding types.

Parameters:

  • token (Symbol)

    the token to check for types.



124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/antelope/grammar/productions.rb', line 124

def type_for(token)
  token = find_token(token) unless token.is_a?(Token)

  case token
  when Token::Nonterminal
    token.type
  when Token::Terminal
    token.type
  when Token::Epsilon
    ""
  when Token::Error
    ""
  end
end