Class: SXP::Reader

Inherits:
Object show all
Includes:
Enumerable
Defined in:
lib/sxp/reader.rb,
lib/sxp/reader/basic.rb,
lib/sxp/reader/scheme.rb,
lib/sxp/reader/sparql.rb,
lib/sxp/reader/extended.rb,
lib/sxp/reader/common_lisp.rb

Overview

The base class for S-expression parsers.

Direct Known Subclasses

Reader::Basic

Defined Under Namespace

Classes: Basic, CommonLisp, EOF, Error, Extended, SPARQL, Scheme

Instance Attribute Summary (collapse)

Class Method Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Reader) initialize(input, options = {}, &block)

Initializes the reader.

Parameters:



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/sxp/reader.rb', line 74

def initialize(input, options = {}, &block)
  @options = options.dup

  case
    when [:getc, :ungetc, :eof?].all? { |x| input.respond_to?(x) }
      @input = input
    when input.respond_to?(:to_str)
      require 'stringio' unless defined?(StringIO)
      # NOTE: StringIO#ungetc mutates the string, so we use #dup to take a copy.
      @input = StringIO.new(input.to_str.dup)
    else
      raise ArgumentError, "expected an IO or String input stream, but got #{input.inspect}"
  end

  if block_given?
    case block.arity
      when 1 then block.call(self)
      else self.instance_eval(&block)
    end
  end
end

Instance Attribute Details

- (Object) input (readonly)

Returns:



97
98
99
# File 'lib/sxp/reader.rb', line 97

def input
  @input
end

- (Hash) options (readonly)

Returns:

  • (Hash)


100
101
102
# File 'lib/sxp/reader.rb', line 100

def options
  @options
end

Class Method Details

+ (Object) read(input, options = {})

Reads one S-expression from the given input stream.

Parameters:

Returns:



65
66
67
# File 'lib/sxp/reader.rb', line 65

def self.read(input, options = {})
  self.new(input, options).read
end

+ (Enumerable<Object>) read_all(input, options = {})

Reads all S-expressions from the given input stream.

Parameters:

Returns:



55
56
57
# File 'lib/sxp/reader.rb', line 55

def self.read_all(input, options = {})
  self.new(input, options).read_all
end

+ (Enumerable<Object>) read_file(filename, options = {})

Reads all S-expressions from a given input file.

Parameters:

Returns:



45
46
47
# File 'lib/sxp/reader.rb', line 45

def self.read_file(filename, options = {})
  File.open(filename.to_s, 'rb') { |io| read_all(io, options) }
end

+ (Enumerable<Object>) read_files(*filenames)

Reads all S-expressions from the given input files.

Parameters:

Returns:



34
35
36
37
# File 'lib/sxp/reader.rb', line 34

def self.read_files(*filenames)
  options = filenames.last.is_a?(Hash) ? filenames.pop : {}
  filenames.map { |filename| read_file(filename, options) }.inject { |sxps, sxp| sxps + sxp }
end

+ (Enumerable<Object>) read_url(url, options = {})

Reads all S-expressions from a given input URL using the HTTP or FTP protocols.

Parameters:

Returns:



23
24
25
26
# File 'lib/sxp/reader.rb', line 23

def self.read_url(url, options = {})
  require 'open-uri'
  open(url.to_s, 'rb', nil, options) { |io| read_all(io, options) }
end

Instance Method Details

- (Enumerator) each {|object| ... }

Yields:

  • (object)

Yield Parameters:

Returns:

  • (Enumerator)


106
107
108
109
110
111
112
# File 'lib/sxp/reader.rb', line 106

def each(&block)
  unless block_given?
    to_enum
  else
    read_all.each(&block) # TODO: lazy reading
  end
end

- (Boolean) eof? (protected)

Returns:

  • (Boolean)


258
259
260
# File 'lib/sxp/reader.rb', line 258

def eof?
  @input.eof?
end

- (String) peek_char (protected)

Returns:



250
251
252
253
254
# File 'lib/sxp/reader.rb', line 250

def peek_char
  char = @input.getc
  @input.ungetc(char) unless char.nil?
  char
end

- (Object) read(options = {}) Also known as: skip

Parameters:

Returns:



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/sxp/reader.rb', line 128

def read(options = {})
  skip_comments
  token, value = read_token
  case token
    when :eof
      throw :eof if options[:eof] == :throw
      raise EOF, "unexpected end of input"
    when :list
      if self.class.const_get(:LPARENS).include?(value)
        read_list
      else
        throw :eol if options[:eol] == :throw # end of list
        raise Error, "unexpected list terminator: ?#{value.chr}"
      end
    else value
  end
end

- (Array) read_all(options = {})

Parameters:

Returns:



117
118
119
120
121
122
123
# File 'lib/sxp/reader.rb', line 117

def read_all(options = {})
  list = []
  catch (:eof) do
    list << read(options.merge(:eof => :throw)) until eof?
  end
  list
end

- (Object) read_atom

Returns:

Raises:

  • (NotImplementedError)


186
187
188
# File 'lib/sxp/reader.rb', line 186

def read_atom
  raise NotImplementedError.new("#{self.class}#read_atom")
end

- (String) read_char (protected) Also known as: skip_char

Returns:

Raises:



240
241
242
243
244
# File 'lib/sxp/reader.rb', line 240

def read_char
  char = @input.getc
  raise EOF, 'unexpected end of input' if char.nil?
  char
end

- (String) read_character

Returns:

Raises:

  • (NotImplementedError)


198
199
200
# File 'lib/sxp/reader.rb', line 198

def read_character
  raise NotImplementedError.new("#{self.class}#read_character")
end

- (String) read_chars(count = 1) (protected)

Parameters:

Returns:



232
233
234
235
236
# File 'lib/sxp/reader.rb', line 232

def read_chars(count = 1)
  buffer = ''
  count.times { buffer << read_char.chr }
  buffer
end

- (Integer) read_integer(base = 10)

Parameters:

Returns:



176
177
178
179
180
181
182
# File 'lib/sxp/reader.rb', line 176

def read_integer(base = 10)
  case buffer = read_literal
    when self.class.const_get(:INTEGER_BASE_#{base}")
      buffer.to_i(base)
    else raise Error, "illegal base-#{base} number syntax: #{buffer}"
  end
end

- (Object) read_list

Parameters:



150
151
152
153
154
155
156
# File 'lib/sxp/reader.rb', line 150

def read_list
  list = []
  catch (:eol) do
    list << read(:eol => :throw) while true
  end
  list
end

- (String) read_literal

Returns:

Raises:

  • (NotImplementedError)


204
205
206
# File 'lib/sxp/reader.rb', line 204

def read_literal
  raise NotImplementedError.new("#{self.class}#read_literal")
end

- (Object) read_sharp

Returns:

Raises:

  • (NotImplementedError)


169
170
171
# File 'lib/sxp/reader.rb', line 169

def read_sharp
  raise NotImplementedError.new("#{self.class}#read_sharp")
end

- (String) read_string

Returns:

Raises:

  • (NotImplementedError)


192
193
194
# File 'lib/sxp/reader.rb', line 192

def read_string
  raise NotImplementedError.new("#{self.class}#read_string")
end

- (Object) read_token

Returns:



160
161
162
163
164
165
# File 'lib/sxp/reader.rb', line 160

def read_token
  case peek_char
    when nil    then :eof
    else [:atom, read_atom]
  end
end

- skip_comments (protected)

This method returns an undefined value.



212
213
214
215
216
217
218
219
# File 'lib/sxp/reader.rb', line 212

def skip_comments
  until eof?
    case (char = peek_char).chr
      when /\s+/ then skip_char
      else break
    end
  end
end

- skip_line (protected)

This method returns an undefined value.



223
224
225
226
227
# File 'lib/sxp/reader.rb', line 223

def skip_line
  loop do
    break if eof? || read_char.chr == $/
  end
end