class RbScheme::Parser

Constants

EOF

Public Class Methods

new(input) click to toggle source
# File lib/rb-scheme/parser.rb, line 15
def initialize(input)
  @input = input
end
read_expr(input) click to toggle source
# File lib/rb-scheme/parser.rb, line 11
def self.read_expr(input)
  new(input).read_expr
end

Public Instance Methods

read_expr() click to toggle source
# File lib/rb-scheme/parser.rb, line 19
def read_expr
  loop do
    c = getc
    case c
    when /\s/
      next
    when EOF
      return nil
    when ';'
      skip_line
      next
    when '('
      return read_list
    when ')'
      return LCloseParen.instance
    when '.'
      return LDot.instance
    when '\''
      return read_quote
    when /\d/
      return LInt.new(read_number(c.to_i))
    when negative_number_pred
      return LInt.new(-read_number(c.to_i))
    when '#'
      return read_hash
    when symbol_rp
      return read_symbol(c)
    else
      raise "Unexpected character - #{c}"
    end
  end
end

Private Instance Methods

negative_number_pred() click to toggle source
# File lib/rb-scheme/parser.rb, line 137
def negative_number_pred
  Proc.new {|c| '-' == c && /\d/ === peek}
end
peek() click to toggle source
# File lib/rb-scheme/parser.rb, line 54
def peek
  c = getc
  @input.ungetc(c)
  c
end
read_hash() click to toggle source
# File lib/rb-scheme/parser.rb, line 117
def read_hash
  c = getc
  case c
  when 't'
    LTrue.instance
  when 'f'
    LFalse.instance
  else
    raise "Unexpected hash literal #{c}"
  end
end
read_list() click to toggle source
# File lib/rb-scheme/parser.rb, line 78
def read_list
  acc = list
  loop do
    obj = read_expr
    raise "read_list: Unclosed parenthesis" if obj.nil?

    case obj
    when LCloseParen
      return reverse_list(acc)
    when LDot
      last = read_expr
      close = read_expr
      if close.nil? || !(LCloseParen === close)
        raise "read_list: Unclosed parenthesis"
      end
      if acc.null?
        raise "read_list: dotted list must have car"
      end

      return acc.reduce(last) { |res, e| cons(e, res) }
    else
      acc = cons(obj, acc)
    end
  end
end
read_number(value) click to toggle source
# File lib/rb-scheme/parser.rb, line 109
def read_number(value)
  result = value
  while /\d/ === peek
    result = result * 10 + getc.to_i
  end
  result
end
read_quote() click to toggle source
# File lib/rb-scheme/parser.rb, line 104
def read_quote
  sym = intern("quote")
  list(sym, read_expr)
end
read_symbol(first_char) click to toggle source
# File lib/rb-scheme/parser.rb, line 129
def read_symbol(first_char)
  result = first_char
  while symbol_rp === peek
    result += getc
  end
  intern(result)
end
reverse_list(lst) click to toggle source
# File lib/rb-scheme/parser.rb, line 60
def reverse_list(lst)
  return lst if lst.null?
  lst.reduce(list) { |res, e| cons(e, res) }
end
skip_line() click to toggle source
# File lib/rb-scheme/parser.rb, line 65
def skip_line
  loop do
    c = getc
    case c
    when EOF, "\n"
      return
    when "\r"
      getc if "\n" == peek
      return
    end
  end
end
symbol_rp() click to toggle source
# File lib/rb-scheme/parser.rb, line 141
def symbol_rp
  allowed = '~!@$%^&*-_=+:/?<>'
  Regexp.new("[A-Za-z0-9#{Regexp.escape(allowed)}]")
end