class RubyLisp::Reader

Constants

TOKEN_REGEX

from kanaka/mal

Attributes

position[RW]
tokens[RW]

Public Class Methods

read_str(str) click to toggle source
# File lib/rubylisp/reader.rb, line 197
def Reader.read_str str
  reader = Reader.new
  reader.tokenize(str)
  forms = []
  while reader.position < reader.tokens.count
    forms << reader.read_form
  end
  forms
end

Public Instance Methods

next_token() click to toggle source
# File lib/rubylisp/reader.rb, line 36
def next_token
  token = peek
  @position += 1
  token
end
peek() click to toggle source
# File lib/rubylisp/reader.rb, line 32
def peek
  @tokens[@position]
end
read_atom() click to toggle source
# File lib/rubylisp/reader.rb, line 67
def read_atom
  token = next_token
  case token
  when nil
    nil
  when /^\-?\d+$/
    Value.new(token.to_i)
  when /^\-?\d+\.\d+$/
    Value.new(token.to_f)
  when /^".*"$/
    # it's safe to use eval here because the tokenizer ensures that
    # the token is an escaped string representation
    Value.new(eval(token))
  # it's a little weird that an unfinished string (e.g. "abc) gets
  # tokenized as "", but at least the behavior is consistent ¯\_(ツ)_/¯
  when ""
    raise ParseError, "Unexpected EOF while parsing string."
  when /^:/
    Value.new(token[1..-1].to_sym)
  when 'nil'
    Value.new(nil)
  when 'true'
    Value.new(true)
  when 'false'
    Value.new(false)
  else
    Symbol.new(token)
  end
end
read_deref_form() click to toggle source
# File lib/rubylisp/reader.rb, line 121
def read_deref_form
  read_special_form 'deref'
end
read_form() click to toggle source
# File lib/rubylisp/reader.rb, line 148
def read_form
  case peek
  when /^;/
    # ignore comments
    next_token
    read_form
  when '('
    next_token
    read_list
  when '['
    next_token
    read_vector
  when '{'
    next_token
    read_hashmap
  when ')'
    raise ParseError, "Unexpected ')'."
  when ']'
    raise ParseError, "Unexpected ']'."
  when '}'
    raise ParseError, "Unexpected '}'."
  when "'"
    next_token
    read_quoted_form
  when '`'
    next_token
    read_quasiquoted_form
  when '~'
    next_token
    read_unquoted_form
  when '~@'
    next_token
    read_splice_unquoted_form
  when '@'
    next_token
    read_deref_form
  when '^'
    next_token
    read_form_with_metadata
  else
    read_atom
  end
end
read_form_with_metadata() click to toggle source
# File lib/rubylisp/reader.rb, line 125
def read_form_with_metadata
  token = peek
  case token
  when nil
    raise ParseError, "Unexpected EOF while parsing metadata."
  when '{'
    next_token
    metadata = read_hashmap
  when /^:/
    kw = read_form
    metadata = hash_map [kw, Value.new(true)]
  else
    raise ParseError, "Invalid metadata: '#{token}'"
  end

  form = read_form
  unless form
    raise ParseError, "Unexpected EOF after metadata."
  end

  list [Symbol.new("with-meta"), form, metadata]
end
read_hashmap() click to toggle source
# File lib/rubylisp/reader.rb, line 63
def read_hashmap
  read_seq 'hash-map', RubyLisp.method(:hash_map), '}'
end
read_list() click to toggle source
# File lib/rubylisp/reader.rb, line 55
def read_list
  read_seq 'list', RubyLisp.method(:list), ')'
end
read_quasiquoted_form() click to toggle source
# File lib/rubylisp/reader.rb, line 109
def read_quasiquoted_form
  read_special_form 'quasiquote'
end
read_quoted_form() click to toggle source
# File lib/rubylisp/reader.rb, line 105
def read_quoted_form
  read_special_form 'quote'
end
read_seq(type_name, constructor, end_token, seq=[]) click to toggle source
# File lib/rubylisp/reader.rb, line 42
def read_seq(type_name, constructor, end_token, seq=[])
  case peek
  when nil
    raise ParseError, "Unexpected EOF while parsing #{type_name}."
  when end_token
    next_token
    constructor.call(seq)
  else
    seq << read_form
    read_seq(type_name, constructor, end_token, seq)
  end
end
read_special_form(special) click to toggle source
# File lib/rubylisp/reader.rb, line 97
def read_special_form(special)
  form = read_form
  unless form
    raise ParseError, "Unexpected EOF while parsing #{special} form."
  end
  list [Symbol.new(special), form]
end
read_splice_unquoted_form() click to toggle source
# File lib/rubylisp/reader.rb, line 117
def read_splice_unquoted_form
  read_special_form 'splice-unquote'
end
read_unquoted_form() click to toggle source
# File lib/rubylisp/reader.rb, line 113
def read_unquoted_form
  read_special_form 'unquote'
end
read_vector() click to toggle source
# File lib/rubylisp/reader.rb, line 59
def read_vector
  read_seq 'vector', RubyLisp.method(:vec), ']'
end
tokenize(str) click to toggle source
# File lib/rubylisp/reader.rb, line 192
def tokenize str
  @tokens = str.strip.scan(TOKEN_REGEX).flatten[0...-1]
  @position = 0
end