Class: Aspen::CustomGrammar::Compiler

Inherits:
Object
  • Object
show all
Defined in:
lib/aspen/custom_grammar/compiler.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root, environment = {}) ⇒ Compiler

Returns a new instance of Compiler.



15
16
17
18
19
20
# File 'lib/aspen/custom_grammar/compiler.rb', line 15

def initialize(root, environment = {})
  @root = root
  @environment = environment
  @type_registry  = {}
  @label_registry = {}
end

Instance Attribute Details

#environmentObject (readonly)

Returns the value of attribute environment.



5
6
7
# File 'lib/aspen/custom_grammar/compiler.rb', line 5

def environment
  @environment
end

#rootObject (readonly)

Returns the value of attribute root.



5
6
7
# File 'lib/aspen/custom_grammar/compiler.rb', line 5

def root
  @root
end

Class Method Details

.compile(root, environment = {}) ⇒ Object



11
12
13
# File 'lib/aspen/custom_grammar/compiler.rb', line 11

def self.compile(root, environment = {})
  new(root, environment).compile
end

.render(root, environment = {}) ⇒ Object



7
8
9
# File 'lib/aspen/custom_grammar/compiler.rb', line 7

def self.render(root, environment = {})
  new(root, environment).render
end

Instance Method Details

#compileObject



22
23
24
25
26
27
28
29
30
31
# File 'lib/aspen/custom_grammar/compiler.rb', line 22

def compile
  # Call #render before accessing the registry. If this code
  # changes, we may need more process control to ensure #render
  # goes first.
  {
    pattern: render,
    type_registry: @type_registry,
    label_registry: @label_registry
  }
end

#renderObject



33
34
35
# File 'lib/aspen/custom_grammar/compiler.rb', line 33

def render
  visit(root)
end

#visit(node) ⇒ Object



37
38
39
40
41
42
43
# File 'lib/aspen/custom_grammar/compiler.rb', line 37

def visit(node)
  short_name = node.class.to_s.split('::').last.downcase
  method_name = "visit_#{short_name}"
  # puts "---- #{method_name}"
  # puts node.inspect
  send(method_name, node)
end

#visit_bare(node) ⇒ Object



70
71
72
# File 'lib/aspen/custom_grammar/compiler.rb', line 70

def visit_bare(node)
  visit(node.content)
end

#visit_capturesegment(node) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/aspen/custom_grammar/compiler.rb', line 53

def visit_capturesegment(node)
  value = case node.type
  when :integer then /(?<#{node.var_name}>[\d,]+\d*)/    # No decimal
  when :float   then /(?<#{node.var_name}>[\d,]+\.\d+)/  # Decimal point required
  when :numeric then /(?<#{node.var_name}>[\d,]+\.?\d*)/ # Optional decimal
  when :string  then /(?<#{node.var_name}>.*?)/
  when :node    then /(?<#{node.var_name}>.*?)/
  else
    raise ArgumentError, "No regexp pattern for type \"#{node.type}\"."
  end
  # Add type to type registry
  @type_registry[node.var_name] = node.type
  # Add label to label registry
  @label_registry[node.var_name] = node.label if node.type == :node
  return value
end

#visit_content(node) ⇒ Object



74
75
76
# File 'lib/aspen/custom_grammar/compiler.rb', line 74

def visit_content(node)
  node.content
end

#visit_expression(node) ⇒ Object



45
46
47
48
49
50
51
# File 'lib/aspen/custom_grammar/compiler.rb', line 45

def visit_expression(node)
  segments = node.segments.map { |segment| visit(segment) }
  segments.last.gsub!(/\.$/, '') # Make the last period optional? Maybe?
  segments.unshift "^"           # Add a bol matcher to the front.
  segments.push "\\.?$"          # Make the last period optional? Again?
  Regexp.new(segments.join)
end