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

Inherits:
Object
  • Object
show all
Defined in:
lib/json5/generated_parser.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.

RBS:

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



650
651
652
653
654
# File 'lib/json5/generated_parser.rb', line 650

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.

RBS:

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

Raises:

  • (ArgumentError)


714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
# File 'lib/json5/generated_parser.rb', line 714

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.

RBS:

  • (child skipped) -> bool



760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
# File 'lib/json5/generated_parser.rb', line 760

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.

RBS:

  • (Array[GreenTrivia] trivia) -> bool



778
779
780
781
782
783
784
785
786
787
788
789
790
791
# File 'lib/json5/generated_parser.rb', line 778

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

RBS:

  • () -> Array[child]



805
# File 'lib/json5/generated_parser.rb', line 805

def elements = @stack.dup.freeze

#finish_source_file(eof_token, incomplete: false) ⇒ Object

Wrap the completed start node and explicit EOF token.

RBS:

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

Raises:

  • (ArgumentError)


732
733
734
735
736
737
738
739
740
741
742
743
# File 'lib/json5/generated_parser.rb', line 732

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.

RBS:

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



747
748
749
750
751
752
753
754
755
756
# File 'lib/json5/generated_parser.rb', line 747

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

RBS:

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



703
704
705
706
707
708
709
710
# File 'lib/json5/generated_parser.rb', line 703

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.

RBS:

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



667
668
669
670
671
# File 'lib/json5/generated_parser.rb', line 667

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

RBS:

  • (Integer expected_kind) -> GreenToken



693
694
695
696
697
698
699
700
# File 'lib/json5/generated_parser.rb', line 693

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

RBS:

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

Raises:

  • (ArgumentError)


674
675
676
677
678
679
680
681
682
683
# File 'lib/json5/generated_parser.rb', line 674

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

RBS:

  • (Array[child] elements) -> void



797
798
799
# File 'lib/json5/generated_parser.rb', line 797

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

#sizeObject

RBS:

  • () -> Integer



802
# File 'lib/json5/generated_parser.rb', line 802

def size = @stack.length

#snapshotObject

RBS:

  • () -> Array[child]



794
# File 'lib/json5/generated_parser.rb', line 794

def snapshot = @stack.dup

#subtree(value) ⇒ Object

Push one already validated nonterminal without rebuilding its descendants.

RBS:

  • (GreenNode value) -> GreenNode



687
688
689
690
# File 'lib/json5/generated_parser.rb', line 687

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

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

RBS:

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



658
659
660
661
662
# File 'lib/json5/generated_parser.rb', line 658

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