class Jsonerino::Lexer

Constants

SIMPLE_ESCAPE_SEQUENCES_MATCHER
SIMPLE_TOKENS_MATCHER

Attributes

i[R]

Public Class Methods

new(contents) click to toggle source
# File lib/jsonerino/lexer.rb, line 26
def initialize(contents)
  @contents = contents
  @i = 0
  @l = 1 # Line number (row)
  @li = 1 # Character in current line (column)
  @c = contents[@i]
end

Public Instance Methods

next_token() click to toggle source
# File lib/jsonerino/lexer.rb, line 34
def next_token
  skip_whitespace

  while more_content?

    v = collect_simple_token
    return v if v

    # Numbers in JSON are only allowed to start with a minus sign
    return collect_number if number?

    return collect_string if @c == '"'

    return collect_id if Helpers.alphanumeric?(@c)

    raise "Unexpected character '#{@c}'"
  end
  nil
end

Private Instance Methods

advance() click to toggle source
# File lib/jsonerino/lexer.rb, line 70
def advance
  return unless more_content?

  @i += 1
  @li += 1
  c = @c
  @c = @contents[@i]
  c
end
collect_escape_sequence() click to toggle source
# File lib/jsonerino/lexer.rb, line 112
def collect_escape_sequence
  advance

  if (escape = Lexer::SIMPLE_ESCAPE_SEQUENCES_MATCHER.find { |x| x[0] == @c })
    escape[1]
  elsif @c == 'u'
    collect_unicode_escape
  else
    @c
  end
end
collect_id() click to toggle source
# File lib/jsonerino/lexer.rb, line 80
def collect_id
  start_i = @li
  str = ''
  while @c && Helpers.alphanumeric?(@c)
    str += @c
    advance
  end
  Token.new Token::TOKEN_ID, str, start_i, @li, @l
end
collect_number() click to toggle source
# File lib/jsonerino/lexer.rb, line 134
def collect_number
  start_i = @li
  str_number = ''
  loop do
    str_number += @c
    advance
    break unless @c && (Helpers.numeric?(@c) || ['.', '-', '+', 'e', 'E'].include?(@c))
  end
  number = str_number.match?(/[\.e]/i) ? str_number.to_f : str_number.to_i
  Token.new Token::TOKEN_NUMBER, number, start_i, @li, @l
end
collect_simple_token() click to toggle source
# File lib/jsonerino/lexer.rb, line 90
def collect_simple_token
  start_i = @li
  token = Lexer::SIMPLE_TOKENS_MATCHER.find { |x| x[0] == @c }
  return nil unless token

  Token.new token[1], advance, start_i, @li, @l
end
collect_string() click to toggle source
# File lib/jsonerino/lexer.rb, line 98
def collect_string
  start_i = @li
  advance
  str = ''

  while @c && @c != '"'
    str += @c == '\\' ? collect_escape_sequence : @c
    advance
  end
  advance

  Token.new Token::TOKEN_STRING, str, start_i, @li, @l
end
collect_unicode_escape() click to toggle source
# File lib/jsonerino/lexer.rb, line 124
def collect_unicode_escape
  str_number = 4.times.inject('0x') do |str|
    advance
    raise "Invalid unicode escape sequence #{@c}" unless Helpers.hex?(@c)

    str + @c
  end
  str_number.to_i.chr
end
more_content?() click to toggle source
# File lib/jsonerino/lexer.rb, line 146
def more_content?
  @i < @contents.length
end
number?() click to toggle source
# File lib/jsonerino/lexer.rb, line 56
def number?
  Helpers.numeric?(@c) || @c == '-'
end
skip_whitespace() click to toggle source
# File lib/jsonerino/lexer.rb, line 60
def skip_whitespace
  while [' ', "\n", "\t"].include?(@c)
    c = advance
    if c == "\n"
      @li = 1
      @l += 1
    end
  end
end