Class: Calyx::Registry

Inherits:
Object
  • Object
show all
Defined in:
lib/calyx/registry.rb

Overview

Lookup table of all the available rules in the grammar.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeRegistry

Construct an empty registry.



7
8
9
10
11
12
13
# File 'lib/calyx/registry.rb', line 7

def initialize
  @options = Options.new({})
  @rules = {}
  @dicts = {}
  @transforms = {}
  @modifiers = Modifiers.new
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *productions) ⇒ Object

Registers a new grammar rule without explicitly calling the ‘#rule` method.

Parameters:

  • name (Symbol)
  • productions (Array)


54
55
56
# File 'lib/calyx/registry.rb', line 54

def method_missing(name, *productions)
  define_rule(name, caller_locations.first, productions)
end

Instance Attribute Details

#dictsObject (readonly)

Returns the value of attribute dicts.



4
5
6
# File 'lib/calyx/registry.rb', line 4

def dicts
  @dicts
end

#modifiersObject (readonly)

Returns the value of attribute modifiers.



4
5
6
# File 'lib/calyx/registry.rb', line 4

def modifiers
  @modifiers
end

#rulesObject (readonly)

Returns the value of attribute rules.



4
5
6
# File 'lib/calyx/registry.rb', line 4

def rules
  @rules
end

#transformsObject (readonly)

Returns the value of attribute transforms.



4
5
6
# File 'lib/calyx/registry.rb', line 4

def transforms
  @transforms
end

Instance Method Details

#combine(registry) ⇒ Object

Merges the given registry instance with the target registry.

This is only needed at compile time, so that child classes can easily inherit the set of rules decared by their parent.

Parameters:



181
182
183
# File 'lib/calyx/registry.rb', line 181

def combine(registry)
  @rules = rules.merge(registry.rules)
end

#define_context_rule(name, trace, productions) ⇒ Object

Defines a rule in the temporary evaluation context.

Parameters:

  • name (Symbol)
  • productions (Array)


88
89
90
91
# File 'lib/calyx/registry.rb', line 88

def define_context_rule(name, trace, productions)
  productions = [productions] unless productions.is_a?(Enumerable)
  context[name.to_sym] = Rule.new(name.to_sym, Rule.build_ast(productions, self), trace)
end

#define_rule(name, trace, productions) ⇒ Object

Defines a static rule in the grammar.

Parameters:

  • name (Symbol)
  • productions (Array)


70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/calyx/registry.rb', line 70

def define_rule(name, trace, productions)
  symbol = name.to_sym

  # TODO: this could be tidied up by consolidating parsing in a single class
  branch = Rule.build_ast(productions, self)

  # If the static rule is a map of k=>v pairs then add it to the lookup dict
  if branch.is_a?(Production::AffixTable)
    dicts[symbol] = branch
  else
    rules[symbol] = Rule.new(symbol, branch, trace)
  end
end

#evaluate(start_symbol = :start, rules_map = {}) ⇒ Array

Evaluates the grammar defined in this registry, combining it with rules from the passed in context.

Produces a syntax tree of nested list nodes.

Parameters:

  • start_symbol (Symbol) (defaults to: :start)
  • rules_map (Hash) (defaults to: {})

Returns:

  • (Array)


193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/calyx/registry.rb', line 193

def evaluate(start_symbol=:start, rules_map={})
  reset_evaluation_context

  rules_map.each do |key, value|
    if rules.key?(key.to_sym)
      raise Errors::DuplicateRule.new(key)
    end

    define_context_rule(key, caller_locations.last, value)
  end

  [start_symbol, expand(start_symbol).evaluate(@options)]
end

#expand(symbol) ⇒ Calyx::Rule

Expands the given symbol to its rule.

Parameters:

  • symbol (Symbol)

Returns:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/calyx/registry.rb', line 97

def expand(symbol)
  expansion = rules[symbol] || context[symbol]

  if expansion.nil?
    if @options.strict?
      raise Errors::UndefinedRule.new(@last_expansion, symbol)
    else
      expansion = Syntax::Terminal.new('')
    end
  end

  @last_expansion = expansion
  expansion
end

#expand_filter(name, value) ⇒ String

Applies the given modifier function to the given value to filter it.

Parameters:

  • name (Symbol)
  • value (String)

Returns:

  • (String)


117
118
119
120
121
122
123
# File 'lib/calyx/registry.rb', line 117

def expand_filter(name, value)
  if transforms.key?(name)
    transforms[name].call(value)
  else
    modifiers.transform(name, value)
  end
end

#expand_map(name, value, direction) ⇒ String

Applies a modifier to substitute the value with a bidirectional map lookup.

Parameters:

  • name (Symbol)
  • value (String)
  • direction (Symbol)

    :left or :right

Returns:

  • (String)


132
133
134
135
136
137
138
139
140
# File 'lib/calyx/registry.rb', line 132

def expand_map(name, value, direction)
  map_lookup = dicts[name]

  if direction == :left
    map_lookup.key_for(value)
  else
    map_lookup.value_for(value)
  end
end

#filter(name, callable = nil) {|String| ... } ⇒ Object

Registers the given block as a string filter.

Parameters:

  • name (Symbol)

Yields:

  • (String)

Yield Returns:

  • (String)


42
43
44
45
46
47
48
# File 'lib/calyx/registry.rb', line 42

def filter(name, callable=nil, &block)
  if block_given?
    transforms[name.to_sym] = block
  else
    transforms[name.to_sym] = callable
  end
end

#mapping(name, pairs) ⇒ Object

Registers a paired mapping regex.

Parameters:

  • name (Symbol)
  • pairs (Hash<Regex,String>)


33
34
35
# File 'lib/calyx/registry.rb', line 33

def mapping(name, pairs)
  transforms[name.to_sym] = construct_mapping(pairs)
end

#memoize_expansion(symbol) ⇒ Object

Expands a memoized rule symbol by evaluating it and storing the result for later.

Parameters:

  • symbol (Symbol)


146
147
148
# File 'lib/calyx/registry.rb', line 146

def memoize_expansion(symbol)
  memos[symbol] ||= expand(symbol).evaluate(@options)
end

#modifier(name) ⇒ Object

Attaches a modifier module to this instance.

Parameters:

  • name (Module)


25
26
27
# File 'lib/calyx/registry.rb', line 25

def modifier(name)
  modifiers.extend(name)
end

#options(opts) ⇒ Object

Applies additional config options to this instance.

Parameters:



18
19
20
# File 'lib/calyx/registry.rb', line 18

def options(opts)
  @options = @options.merge(opts)
end

#rule(name, *productions) ⇒ Object

Registers a new grammar rule.

Parameters:

  • name (Symbol)
  • productions (Array)


62
63
64
# File 'lib/calyx/registry.rb', line 62

def rule(name, *productions)
  define_rule(name, caller_locations.first, productions)
end

#unique_expansion(symbol) ⇒ Object

Expands a unique rule symbol by evaluating it and checking that it hasn’t previously been selected.

Parameters:

  • symbol (Symbol)


154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/calyx/registry.rb', line 154

def unique_expansion(symbol)
  pending = true
  uniques[symbol] = [] if uniques[symbol].nil?

  while pending
    if uniques[symbol].size == expand(symbol).size
      uniques[symbol] = []
      pending = false
    end

    result = expand(symbol).evaluate(@options)

    unless uniques[symbol].include?(result)
      uniques[symbol] << result
      pending = false
    end
  end

  result
end