Class: Parser::Source::Buffer

Inherits:
Object
  • Object
show all
Defined in:
lib/parser/source/buffer.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, first_line = 1) ⇒ Buffer

Returns a new instance of Buffer.



52
53
54
55
56
57
58
59
# File 'lib/parser/source/buffer.rb', line 52

def initialize(name, first_line = 1)
  @name        = name
  @source      = nil
  @first_line  = first_line

  @lines       = nil
  @line_begins = nil
end

Instance Attribute Details

#first_lineObject (readonly)

Returns the value of attribute first_line.



7
8
9
# File 'lib/parser/source/buffer.rb', line 7

def first_line
  @first_line
end

#nameObject (readonly)

Returns the value of attribute name.



7
8
9
# File 'lib/parser/source/buffer.rb', line 7

def name
  @name
end

Class Method Details

.recognize_encoding(string) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/parser/source/buffer.rb', line 9

def self.recognize_encoding(string)
  if string.empty?
    return Encoding::BINARY
  end

  # TODO: Make this more efficient.
  first_line, second_line = string.lines.first(2)

  [first_line, second_line].each do |line|
    line.force_encoding(Encoding::ASCII_8BIT) if line
  end

  if first_line =~ /\A\xef\xbb\xbf/ # BOM
    return Encoding::UTF_8
  elsif first_line[0, 2] == '#!'
    encoding_line = second_line
  else
    encoding_line = first_line
  end

  if encoding_line =~ /^#.*coding[:=]\s*([a-z0-9_-]+)/
    Encoding.find($1)
  else
    string.encoding
  end
end

.reencode_string(string) ⇒ Object

Lexer expects UTF-8 input. This method processes the input in an arbitrary valid Ruby encoding and returns an UTF-8 encoded string.



40
41
42
43
44
45
46
47
48
49
50
# File 'lib/parser/source/buffer.rb', line 40

def self.reencode_string(string)
  encoding = recognize_encoding(string)

  unless encoding.ascii_compatible?
    raise RuntimeError, "Encoding #{encoding} is not ASCII-compatible"
  end

  string.
    force_encoding(encoding).
    encode(Encoding::UTF_8)
end

Instance Method Details

#decompose_position(position) ⇒ Object



90
91
92
93
94
# File 'lib/parser/source/buffer.rb', line 90

def decompose_position(position)
  line_no, line_begin = line_for(position)

  [ @first_line + line_no, position - line_begin ]
end

#line_beginsObject (private)



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/parser/source/buffer.rb', line 114

def line_begins
  unless @line_begins
    @line_begins, index = [ [ 0, 0 ] ], 1

    @source.each_char do |char|
      if char == "\n"
        @line_begins << [ @line_begins.length, index ]
      end

      index += 1
    end

    @line_begins = @line_begins.reverse
  end

  @line_begins
end

#line_for(position) ⇒ Object (private)



132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/parser/source/buffer.rb', line 132

def line_for(position)
  if line_begins.respond_to? :bsearch
    line_begins.bsearch do |line, line_begin|
      line_begin <= position
    end
  else
    # Slower variant for <= Ruby 2.0.
    line_begins.find do |line, line_begin|
      line_begin <= position
    end
  end
end

#readObject



61
62
63
64
65
66
67
# File 'lib/parser/source/buffer.rb', line 61

def read
  File.open(@name, 'rb') do |io|
    self.source = io.read
  end

  self
end

#sourceObject



69
70
71
72
73
74
75
# File 'lib/parser/source/buffer.rb', line 69

def source
  if @source.nil?
    raise RuntimeError, 'Cannot extract source from uninitialized Source::Buffer'
  end

  @source
end

#source=(source) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/parser/source/buffer.rb', line 77

def source=(source)
  if @source
    raise ArgumentError, "Source::Buffer is immutable"
  end

  if source.respond_to? :encoding
    source = source.dup if source.frozen?
    source = self.class.reencode_string(source)
  end

  @source = source.freeze
end

#source_line(line) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/parser/source/buffer.rb', line 96

def source_line(line)
  unless @lines
    @lines = @source.lines.map do |source_line|
      # Line endings will be commonly present for all lines
      # except the last one. It does not make sense to keep them.
      if source_line.end_with? "\n"
        source_line.chomp
      else
        source_line
      end
    end
  end

  @lines[line - @first_line].dup
end