Class: Ibex::Frontend::RuleDocumentation

Inherits:
Object
  • Object
show all
Defined in:
lib/ibex/frontend/rule_documentation.rb,
sig/ibex/frontend/rule_documentation.rbs

Overview

Attaches immediately preceding lossless line-comment blocks to parsed rules.

Constant Summary collapse

EMPTY_SEGMENTS =

Signature:

  • Array[Segment]

Returns:

Array.new(0).freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(document) ⇒ RuleDocumentation

Returns a new instance of RuleDocumentation.

RBS:

  • (SourceDocument document) -> void

Parameters:



16
17
18
19
20
21
# File 'lib/ibex/frontend/rule_documentation.rb', line 16

def initialize(document)
  @segments_by_line = index_segments(document) #: Hash[Integer, Array[Segment]]
  @inline_lines = document.tokens.filter_map do |token|
    token.location.line if token.type == :inline
  end #: Array[Integer]
end

Class Method Details

.enrich(node, document) ⇒ AST::Root, AST::Fragment

RBS:

  • (AST::Root | AST::Fragment node, SourceDocument document) -> (AST::Root | AST::Fragment)

Parameters:

Returns:



11
12
13
# File 'lib/ibex/frontend/rule_documentation.rb', line 11

def self.enrich(node, document)
  new(document).enrich(node)
end

Instance Method Details

#covered_lines(segment) ⇒ Array[Integer]

RBS:

  • (Segment segment) -> Array[Integer]

Parameters:

Returns:

  • (Array[Integer])


95
96
97
98
99
100
# File 'lib/ibex/frontend/rule_documentation.rb', line 95

def covered_lines(segment)
  first = segment.span.start.line
  return [first] if segment.kind == :newline

  (first..segment.span.finish.line).to_a
end

#documentation_anchor(rule) ⇒ Integer

RBS:

  • (AST::Rule rule) -> Integer

Parameters:

Returns:

  • (Integer)


47
48
49
50
51
# File 'lib/ibex/frontend/rule_documentation.rb', line 47

def documentation_anchor(rule)
  return rule.loc.line unless rule.inline

  @inline_lines.reverse_each.find { |line| line <= rule.loc.line } || rule.loc.line
end

#documentation_before(rule_line) ⇒ String?

RBS:

  • (Integer rule_line) -> String?

Parameters:

  • rule_line (Integer)

Returns:

  • (String, nil)


54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/ibex/frontend/rule_documentation.rb', line 54

def documentation_before(rule_line)
  lines = [] #: Array[String]
  line = rule_line - 1
  while line.positive?
    content = documentation_line(line)
    break unless content

    lines.unshift(content)
    line -= 1
  end
  lines.empty? ? nil : lines.join("\n").freeze
end

#documentation_line(line) ⇒ String?

RBS:

  • (Integer line) -> String?

Parameters:

  • line (Integer)

Returns:

  • (String, nil)


68
69
70
71
72
73
74
75
76
77
78
# File 'lib/ibex/frontend/rule_documentation.rb', line 68

def documentation_line(line)
  segments = @segments_by_line.fetch(line, EMPTY_SEGMENTS)
  comments = segments.select { |segment| segment.kind == :line_comment }
  return unless comments.length == 1

  comment = comments.fetch(0)
  return unless comment.text.start_with?("##")
  return unless segments.all? { |segment| documentation_line_segment?(segment, comment) }

  comment.text.delete_prefix("##").delete_prefix(" ")
end

#documentation_line_segment?(segment, comment) ⇒ Boolean

RBS:

  • (Segment segment, Segment comment) -> bool

Parameters:

Returns:

  • (Boolean)


81
82
83
# File 'lib/ibex/frontend/rule_documentation.rb', line 81

def documentation_line_segment?(segment, comment)
  segment.equal?(comment) || %i[whitespace newline].include?(segment.kind)
end

#documented_rule(rule) ⇒ AST::Rule

RBS:

  • (AST::Rule rule) -> AST::Rule

Parameters:

Returns:



39
40
41
42
43
44
# File 'lib/ibex/frontend/rule_documentation.rb', line 39

def documented_rule(rule)
  AST::Rule.new(
    lhs: rule.lhs, parameters: rule.parameters, alternatives: rule.alternatives, loc: rule.loc,
    documentation: documentation_before(documentation_anchor(rule)), inline: rule.inline
  )
end

#enrich(node) ⇒ AST::Root, AST::Fragment

RBS:

  • (AST::Root | AST::Fragment node) -> (AST::Root | AST::Fragment)

Parameters:

Returns:



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/ibex/frontend/rule_documentation.rb', line 24

def enrich(node)
  rules = node.rules.map { |rule| documented_rule(rule) }
  if node.is_a?(AST::Root)
    AST::Root.new(
      class_name: node.class_name, superclass: node.superclass, declarations: node.declarations,
      rules: rules, user_code: node.user_code, loc: node.loc, extended: node.extended, cst: node.cst
    )
  else
    AST::Fragment.new(declarations: node.declarations, rules: rules, loc: node.loc)
  end
end

#index_segments(document) ⇒ Hash[Integer, Array[Segment]]

RBS:

  • (SourceDocument document) -> Hash[Integer, Array[Segment]]

Parameters:

Returns:



86
87
88
89
90
91
92
# File 'lib/ibex/frontend/rule_documentation.rb', line 86

def index_segments(document)
  indexed = Hash.new { |hash, line| hash[line] = [] } #: Hash[Integer, Array[Segment]]
  document.cst.each do |segment|
    covered_lines(segment).each { |line| indexed[line] << segment }
  end
  indexed
end