Class: Ibex::Runtime::CST::GreenBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/ibex/runtime/cst/green/builder.rb

Overview

LR bottom-up builder backed by a Green element stack. rubocop:disable Naming/PredicateMethod -- mutation methods report whether they found a target.

Constant Summary collapse

EMPTY_TRIVIA =
empty_trivia.freeze

Instance Method Summary collapse

Constructor Details

#initialize(kinds:, cache: NodeCache.new) ⇒ GreenBuilder

Returns a new instance of GreenBuilder.



21
22
23
24
25
# File 'lib/ibex/runtime/cst/green/builder.rb', line 21

def initialize(kinds:, cache: NodeCache.new)
  @kinds = kinds
  @cache = cache
  @stack = []
end

Instance Method Details

#absorb_into_error(pop_count, skipped: []) ⇒ Object

Preserve popped and discarded Green elements in one error node.

Raises:

  • (ArgumentError)


85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/ibex/runtime/cst/green/builder.rb', line 85

def absorb_into_error(pop_count, skipped: [])
  raise ArgumentError, "pop_count must be non-negative" if pop_count.negative?
  raise ArgumentError, "green stack underflow" if pop_count > @stack.length

  empty = [] #: Array[child]
  popped = pop_count.zero? ? empty : @stack.pop(pop_count)
  value = @cache.intern_node(
    GreenNode.new(
      kind: @kinds.fetch(:error_node), children: popped + skipped,
      flags: Flags::CONTAINS_ERROR
    )
  )
  @stack << value
  value
end

#append_to_last_error(skipped) ⇒ Object

Append discarded input to the most recent error node on the stack.



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/ibex/runtime/cst/green/builder.rb', line 131

def append_to_last_error(skipped)
  index = @stack.rindex { |element| element.is_a?(GreenNode) && element.kind == @kinds.fetch(:error_node) }
  return false unless index

  error = @stack.fetch(index)
  return false unless error.is_a?(GreenNode)

  @stack[index] = @cache.intern_node(
    GreenNode.new(
      kind: error.kind, children: error.children + [skipped],
      flags: error.intrinsic_flags | Flags::CONTAINS_ERROR, annotations: error.annotations
    )
  )
  true
end

#append_trailing_to_last_token(trivia) ⇒ Object

Path-copy the right edge to attach balanced trailing trivia.



149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/ibex/runtime/cst/green/builder.rb', line 149

def append_trailing_to_last_token(trivia)
  return false if trivia.empty?

  index = @stack.length - 1
  while index >= 0
    replacement = with_rightmost_trailing(@stack.fetch(index), trivia)
    if replacement
      @stack[index] = replacement
      return true
    end
    index -= 1
  end
  false
end

#elementsObject



176
# File 'lib/ibex/runtime/cst/green/builder.rb', line 176

def elements = @stack.dup.freeze

#finish_source_file(eof_token, incomplete: false) ⇒ Object

Wrap the completed start node and explicit EOF token.

Raises:

  • (ArgumentError)


103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/ibex/runtime/cst/green/builder.rb', line 103

def finish_source_file(eof_token, incomplete: false)
  raise ArgumentError, "expected one completed start node" unless @stack.one?

  flags = Flags::SYNTHETIC
  flags |= Flags::INCOMPLETE_INPUT if incomplete
  @cache.intern_node(
    GreenNode.new(
      kind: @kinds.fetch(:source_file), children: [@stack.fetch(0), eof_token],
      flags: flags
    )
  )
end

#finish_synthetic_root(trailing = [], incomplete: false) ⇒ Object

Preserve every available fragment when parsing terminates without a start node.



118
119
120
121
122
123
124
125
126
127
# File 'lib/ibex/runtime/cst/green/builder.rb', line 118

def finish_synthetic_root(trailing = [], incomplete: false)
  flags = Flags::SYNTHETIC | Flags::CONTAINS_ERROR
  flags |= Flags::INCOMPLETE_INPUT if incomplete
  @cache.intern_node(
    GreenNode.new(
      kind: @kinds.fetch(:synthetic_root), children: @stack + trailing,
      flags: flags
    )
  )
end

#lexical_error(text, leading: EMPTY_TRIVIA) ⇒ Object



74
75
76
77
78
79
80
81
# File 'lib/ibex/runtime/cst/green/builder.rb', line 74

def lexical_error(text, leading: EMPTY_TRIVIA)
  value = @cache.intern_token_fields(
    kind: @kinds.fetch(:lexical_error_token), text: text, leading: leading,
    flags: Flags::CONTAINS_ERROR
  )
  @stack << value
  value
end

#make_token(kind, text, leading: EMPTY_TRIVIA, trailing: EMPTY_TRIVIA, flags: 0) ⇒ Object

Construct an interned token without changing the builder stack.



38
39
40
41
42
# File 'lib/ibex/runtime/cst/green/builder.rb', line 38

def make_token(kind, text, leading: EMPTY_TRIVIA, trailing: EMPTY_TRIVIA, flags: 0)
  @cache.intern_token_fields(
    kind: kind, text: text, leading: leading, trailing: trailing, flags: flags
  )
end

#missing(expected_kind) ⇒ Object



64
65
66
67
68
69
70
71
# File 'lib/ibex/runtime/cst/green/builder.rb', line 64

def missing(expected_kind)
  value = @cache.intern_token_fields(
    kind: @kinds.fetch(:missing_token), text: "", expected_kind: expected_kind,
    flags: Flags::CONTAINS_MISSING | Flags::SYNTHETIC
  )
  @stack << value
  value
end

#node(kind, arity, flags: 0) ⇒ Object

Raises:

  • (ArgumentError)


45
46
47
48
49
50
51
52
53
54
# File 'lib/ibex/runtime/cst/green/builder.rb', line 45

def node(kind, arity, flags: 0)
  raise ArgumentError, "arity must be non-negative" if arity.negative?
  raise ArgumentError, "green stack underflow" if arity > @stack.length

  empty = [] #: Array[child]
  children = arity.zero? ? empty : @stack.pop(arity)
  value = @cache.intern_node(GreenNode.new(kind: kind, children: children, flags: flags))
  @stack << value
  value
end

#restore(elements) ⇒ Object



168
169
170
# File 'lib/ibex/runtime/cst/green/builder.rb', line 168

def restore(elements)
  @stack = elements.dup
end

#sizeObject



173
# File 'lib/ibex/runtime/cst/green/builder.rb', line 173

def size = @stack.length

#snapshotObject



165
# File 'lib/ibex/runtime/cst/green/builder.rb', line 165

def snapshot = @stack.dup

#subtree(value) ⇒ Object

Push one already validated nonterminal without rebuilding its descendants.



58
59
60
61
# File 'lib/ibex/runtime/cst/green/builder.rb', line 58

def subtree(value)
  @stack << value
  value
end

#token(kind, text, leading: EMPTY_TRIVIA, trailing: EMPTY_TRIVIA, flags: 0) ⇒ Object



29
30
31
32
33
# File 'lib/ibex/runtime/cst/green/builder.rb', line 29

def token(kind, text, leading: EMPTY_TRIVIA, trailing: EMPTY_TRIVIA, flags: 0)
  value = make_token(kind, text, leading: leading, trailing: trailing, flags: flags)
  @stack << value
  value
end