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

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

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 =

Returns:

empty_trivia.freeze

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of GreenBuilder.

RBS:

  • (kinds: Kind, ?cache: NodeCache) -> void

Parameters:



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: []) ⇒ GreenNode

Preserve popped and discarded Green elements in one error node.

RBS:

  • (Integer pop_count, ?skipped: Array[child]) -> GreenNode

Parameters:

  • pop_count (Integer)
  • skipped: (Array[child]) (defaults to: [])

Returns:



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) ⇒ Boolean

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

RBS:

  • (child skipped) -> bool

Parameters:

  • skipped (child)

Returns:

  • (Boolean)


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) ⇒ Boolean

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

RBS:

  • (Array[GreenTrivia] trivia) -> bool

Parameters:

Returns:

  • (Boolean)


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

#elementsArray[child]

RBS:

  • () -> Array[child]

Returns:

  • (Array[child])


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

def elements = @stack.dup.freeze

#finish_source_file(eof_token, incomplete: false) ⇒ GreenNode

Wrap the completed start node and explicit EOF token.

RBS:

  • (GreenToken eof_token, ?incomplete: bool) -> GreenNode

Parameters:

  • eof_token (GreenToken)
  • incomplete: (Boolean) (defaults to: false)

Returns:



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) ⇒ GreenNode

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

RBS:

  • (?Array[child] trailing, ?incomplete: bool) -> GreenNode

Parameters:

  • trailing (Array[child]) (defaults to: [])
  • incomplete: (Boolean) (defaults to: false)

Returns:



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) ⇒ GreenToken

RBS:

  • (String text, ?leading: Array[GreenTrivia]) -> GreenToken

Parameters:

  • text (String)
  • leading: (Array[GreenTrivia]) (defaults to: EMPTY_TRIVIA)

Returns:



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) ⇒ GreenToken

Construct an interned token without changing the builder stack.

RBS:

  • (Integer kind, String text, ?leading: Array[GreenTrivia], ?trailing: Array[GreenTrivia], ?flags: Integer) -> GreenToken

Parameters:

  • kind (Integer)
  • text (String)
  • leading: (Array[GreenTrivia]) (defaults to: EMPTY_TRIVIA)
  • trailing: (Array[GreenTrivia]) (defaults to: EMPTY_TRIVIA)
  • flags: (Integer) (defaults to: 0)

Returns:



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) ⇒ GreenToken

RBS:

  • (Integer expected_kind) -> GreenToken

Parameters:

  • expected_kind (Integer)

Returns:



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) ⇒ GreenNode

RBS:

  • (Integer kind, Integer arity, ?flags: Integer) -> GreenNode

Parameters:

  • kind (Integer)
  • arity (Integer)
  • flags: (Integer) (defaults to: 0)

Returns:



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) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[child] elements) -> void

Parameters:

  • elements (Array[child])


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

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

#sizeInteger

RBS:

  • () -> Integer

Returns:

  • (Integer)


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

def size = @stack.length

#snapshotArray[child]

RBS:

  • () -> Array[child]

Returns:

  • (Array[child])


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

def snapshot = @stack.dup

#subtree(value) ⇒ GreenNode

Push one already validated nonterminal without rebuilding its descendants.

RBS:

  • (GreenNode value) -> GreenNode

Parameters:

Returns:



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) ⇒ GreenToken

RBS:

  • (Integer kind, String text, ?leading: Array[GreenTrivia], ?trailing: Array[GreenTrivia], ?flags: Integer) -> GreenToken

Parameters:

  • kind (Integer)
  • text (String)
  • leading: (Array[GreenTrivia]) (defaults to: EMPTY_TRIVIA)
  • trailing: (Array[GreenTrivia]) (defaults to: EMPTY_TRIVIA)
  • flags: (Integer) (defaults to: 0)

Returns:



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

#with_rightmost_trailing(element, trivia) ⇒ child?

RBS:

  • (child element, Array[GreenTrivia] trivia) -> child?

Parameters:

Returns:

  • (child, nil)


181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/ibex/runtime/cst/green/builder.rb', line 181

def with_rightmost_trailing(element, trivia)
  if element.is_a?(GreenToken)
    combined_trailing = element.trailing.empty? ? trivia : element.trailing + trivia
    return @cache.intern_token_fields(
      kind: element.kind, text: element.text, leading: element.leading,
      trailing: combined_trailing, flags: element.flags,
      expected_kind: element.expected_kind
    )
  end
  return if element.children.empty?

  child = element.children.last
  return unless child

  replacement = with_rightmost_trailing(child, trivia)
  return unless replacement

  children = element.children.dup
  children[-1] = replacement
  @cache.intern_node(
    GreenNode.new(
      kind: element.kind, children: children, flags: element.intrinsic_flags,
      annotations: element.annotations
    )
  )
end