lib/edn/parser.rb in edn-1.0.8 vs lib/edn/parser.rb in edn-1.1.0

- old
+ new

@@ -1,11 +1,21 @@ require 'stringio' require 'set' module EDN + Parser = RubyEdnParser + @parser_class = RubyEdnParser + def self.parser=(p) + @parser_class = p + end + + def self.new_parser(*args) + @parser_class.new(*args) + end + # Object returned when there is nothing to return NOTHING = Object.new # Object to return when we hit end of file. Cant be nil or :eof @@ -75,269 +85,8 @@ func = TAGS[tag] if func func.call(element) else EDN::Type::Unknown.new(tag, element) - end - end - - class Parser - def initialize(source, *extra) - io = source.instance_of?(String) ? StringIO.new(source) : source - @s = CharStream.new(io) - end - - def read(return_nothing=false) - meta = read_meta - value = read_basic(return_nothing) - if meta && value != NOTHING - value.extend EDN::Metadata - value.metadata = meta - end - value - end - - def eof? - @s.eof? - end - - def unknown - raise "Don't know what to do with #{@s.current} #{@s.current.class}" - end - - def read_eof - EOF - end - - def read_char - result = @s.advance - @s.advance - until @s.eof? - break unless @s.digit? || @s.alpha? - result += @s.current - @s.advance - end - - return result if result.size == 1 - - case result - when 'newline' - "\n" - when 'return' - "\r" - when 'tab' - "\t" - when 'space' - " " - else - raise "Unknown char #{result}" - end - end - - def read_slash - @s.advance - Type::Symbol.new('/') - end - - def read_number_or_symbol - leading = @s.current - @s.advance - return read_number(leading) if @s.digit? - read_symbol(leading) - end - - def read_symbol_chars - result = '' - - ch = @s.current - while SYMBOL_INTERIOR_CHARS.include?(ch) - result << ch - ch = @s.advance - end - return result unless @s.skip_past('/') - - result << '/' - ch = @s.current - while SYMBOL_INTERIOR_CHARS.include?(ch) - result << ch - ch = @s.advance - end - - result - end - - def read_extension - @s.advance - if @s.current == '{' - @s.advance - read_collection(Set, '}') - elsif @s.current == "_" - @s.advance - x = read - NOTHING - else - tag = read_symbol_chars - value = read - EDN.tagged_element(tag, value) - end - end - - def read_symbol(leading='') - token = leading + read_symbol_chars - return true if token == "true" - return false if token == "false" - return nil if token == "nil" - Type::Symbol.new(token) - end - - def read_keyword - @s.advance - read_symbol_chars.to_sym - end - - def escape_char(ch) - return '\\' if ch == '\\' - return "\n" if ch == 'n' - return "\t" if ch == 't' - return "\r" if ch == 'r' - ch - end - - def read_string - @s.advance - - result = '' - until @s.current == '"' - raise "Unexpected eof" if @s.eof? - if @s.current == '\\' - @s.advance - result << escape_char(@s.current) - else - result << @s.current - end - @s.advance - end - @s.advance - result - end - - def call_reader(reader) - if reader.instance_of? Symbol - self.send(reader) - else - self.instance_exec(&reader) - end - end - - def read_basic(return_nothing=false) - @s.skip_ws - ch = @s.current - result = call_reader(READERS[ch]) - while NOTHING.equal?(result) && !return_nothing - @s.skip_ws - result = call_reader(READERS[@s.current]) - end - - result - end - - def read_digits(min_digits=0) - result = '' - - if @s.current == '+' || @s.current == '-' - result << @s.current - @s.advance - end - - n_digits = 0 - while @s.current =~ /[0-9]/ - n_digits += 1 - result << @s.current - @s.advance - end - - raise "Expected at least #{min_digits} digits, found #{result}" unless n_digits >= min_digits - result - end - - def finish_float(whole_part) - result = whole_part - - if @s.current == '.' - result += '.' - @s.advance - result = @s.digit? ? result + read_digits : result + '0' - #puts "aaa: #{result}" - end - - if @s.current == 'e' || @s.current == 'E' - @s.advance - result = result + 'e' + read_digits - #puts "bbb: #{result}" - end - #puts result - result.to_f - end - - def read_number(leading='') - result = leading + read_digits - - if %w{. e E}.include? @s.current - return finish_float(result) - elsif @s.skip_past('M') || @s.skip_past('N') - result.to_i - else - result.to_i - end - end - - def read_meta - raw_metadata = [] - @s.skip_ws - while @s.current == '^' - @s.advance - raw_metadata << read_basic - @s.skip_ws - end - - metadata = raw_metadata.reverse.reduce({}) do |acc, m| - case m - when Symbol then acc.merge(m => true) - when EDN::Type::Symbol then acc.merge(:tag => m) - else acc.merge(m) - end - end - metadata.empty? ? nil : metadata - end - - def read_list - @s.advance - read_collection(EDN::Type::List, ')') - end - - def read_vector - @s.advance - read_collection(Array, ']') - end - - def read_map - @s.advance - array = read_collection(Array, '}') - raise "Need an even number of items for a map" unless array.count.even? - Hash[*array] - end - - def read_collection(clazz, closing) - result = clazz.new - - while true - @s.skip_ws - raise "Unexpected eof" if @s.eof? - break if @s.current == closing - next_value = read(true) - result << next_value unless next_value == NOTHING - end - @s.advance - result end end end