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

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

#initialize(input, **options, &block) ⇒ Reader

Initializes the reader.

Parameters:



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/sxp/reader.rb', line 85

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)
      @input.set_encoding('UTF-8') if @input.respond_to?(:set_encoding)
    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

#inputObject (readonly)

Returns:



109
110
111
# File 'lib/sxp/reader.rb', line 109

def input
  @input
end

#optionsHash (readonly)

Returns:



112
113
114
# File 'lib/sxp/reader.rb', line 112

def options
  @options
end

Class Method Details

.read(input, **options) ⇒ Object

Reads one S-expression from the given input stream.

Parameters:

Returns:



76
77
78
# File 'lib/sxp/reader.rb', line 76

def self.read(input, **options)
  self.new(input, **options).read
end

.read_all(input, **options) ⇒ Enumerable<Object>

Reads all S-expressions from the given input stream.

Parameters:

Returns:



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

def self.read_all(input, **options)
  self.new(input, **options).read_all
end

.read_file(filename, **options) ⇒ Enumerable<Object>

Reads all S-expressions from a given input file.

Parameters:

Returns:



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

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

.read_url(url, **options) ⇒ Enumerable<Object>

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

Parameters:

Returns:



25
26
27
28
# File 'lib/sxp/reader.rb', line 25

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

#each {|object| ... } ⇒ Enumerator

Yields:

  • (object)

Yield Parameters:

Returns:

  • (Enumerator)


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

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

#read(eof: nil, eol: nil, list_term: false, **options) ⇒ Object Also known as: skip

Parameters:

Options Hash (**options):

  • :eof (:throw)

    If ‘:throw`, throw `:eof` on end of file.

  • :eol (:throw)

    If ‘:throw`, throw `:eol` on end of line.

  • :list_term (String)

    Expected list terminator; it’s an error if another terminator is found

Returns:



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/sxp/reader.rb', line 148

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

#read_all(**options) ⇒ Array

Parameters:

Returns:



130
131
132
133
134
135
136
# File 'lib/sxp/reader.rb', line 130

def read_all(**options)
  list = []
  catch (:eof) do
    list << read(eof: :throw, **options) until eof?
  end
  list
end

#read_atomObject

Returns:

Raises:

  • (NotImplementedError)


209
210
211
# File 'lib/sxp/reader.rb', line 209

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

#read_characterString

Returns:

Raises:

  • (NotImplementedError)


221
222
223
# File 'lib/sxp/reader.rb', line 221

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

#read_files(*filenames) ⇒ Enumerable<Object> #read_files(*filenames, **options) ⇒ Enumerable<Object>

Reads all S-expressions from the given input files.

Overloads:

  • #read_files(*filenames) ⇒ Enumerable<Object>

    Parameters:

    • filenames (Enumerable<String>)
  • #read_files(*filenames, **options) ⇒ Enumerable<Object>

    Parameters:

Returns:



42
43
44
45
# File 'lib/sxp/reader.rb', line 42

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

#read_integer(base = 10) ⇒ Integer

Parameters:

Returns:



199
200
201
202
203
204
205
# File 'lib/sxp/reader.rb', line 199

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

#read_list(list_term = nil) ⇒ Array

Parameters:

  • list_term (String) (defaults to: nil)

    (nil) String expected to terminate the list

Returns:



173
174
175
176
177
178
179
# File 'lib/sxp/reader.rb', line 173

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

#read_literalString

Returns:

Raises:

  • (NotImplementedError)


227
228
229
# File 'lib/sxp/reader.rb', line 227

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

#read_sharpObject

Returns:

Raises:

  • (NotImplementedError)


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

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

#read_stringString

Returns:

Raises:

  • (NotImplementedError)


215
216
217
# File 'lib/sxp/reader.rb', line 215

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

#read_tokenObject

Returns:



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

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