Class: Clojure::Reader

Inherits:
Object
  • Object
show all
Defined in:
lib/clojure/reader.rb

Overview

read Clojure as Ruby’s data-structures

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source) ⇒ Reader

Returns a new instance of Reader.



6
7
8
9
10
11
12
13
14
15
16
# File 'lib/clojure/reader.rb', line 6

def initialize(source)
  @source = source
  @io = StringIO.new source
  @ast = []
  loop do
    break if eof?

    r = read_next
    @ast << r if r
  end
end

Instance Attribute Details

#astObject (readonly)

Returns the value of attribute ast.



22
23
24
# File 'lib/clojure/reader.rb', line 22

def ast
  @ast
end

Instance Method Details

#cursorObject



24
25
26
# File 'lib/clojure/reader.rb', line 24

def cursor
  @cursor || next_char
end

#eof?Boolean

Returns:

  • (Boolean)


28
29
30
# File 'lib/clojure/reader.rb', line 28

def eof?
  @cursor == :eof
end

#inspectObject



18
19
20
# File 'lib/clojure/reader.rb', line 18

def inspect
  @ast
end

#next_charObject



32
33
34
# File 'lib/clojure/reader.rb', line 32

def next_char
  @cursor = @io.getc || :eof
end

#read_form(till: ")", into: []) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/clojure/reader.rb', line 83

def read_form(till: ")", into: [])
  opening = cursor
  skip_char # opening parenthesis
  ast = into
  until cursor == till
    raise StandardError, "Unbalanced #{opening}#{till}" if eof?

    r = read_next
    ast << r if r
  end
  skip_char # closing parenthesis
  ast
end

#read_keywordObject



107
108
109
110
111
112
# File 'lib/clojure/reader.rb', line 107

def read_keyword
  next_char
  k = cursor
  k << cursor while next_char.match(/\w|\.|#|-|_/)
  k.to_sym
end

#read_nextObject

rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/AbcSize



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/clojure/reader.rb', line 38

def read_next
  # puts "reading next"
  # puts "-> #{cursor}"
  case cursor
  when :eof then nil
  when /\s|,/ then skip_char
  when /;/ then skip_comment
  when /\d/ then read_number
  when /\(/ then read_form
  when /\[/ then read_form till: "]", into: ["vector"]
  when /\{/ then read_form till: "}", into: ["hash-map"]
  when /:/ then read_keyword
  when /"/ then read_string
  when /'/ then read_quote
  when /\#/ then read_special
  when /\S/ then read_symbol
  else raise StandardError, "Unexpected symbol: #{cursor}"
  end
end

#read_numberObject



97
98
99
100
101
102
103
104
105
# File 'lib/clojure/reader.rb', line 97

def read_number
  n = cursor
  n << cursor while next_char.match(/[\d|.]/)
  begin
    Integer(n)
  rescue StandardError
    Float(n)
  end
end

#read_quoteObject



114
115
116
117
# File 'lib/clojure/reader.rb', line 114

def read_quote
  skip_char # '
  ["quote", read_next]
end

#read_sexp_commentObject



67
68
69
70
71
# File 'lib/clojure/reader.rb', line 67

def read_sexp_comment
  next_char
  read_next
  nil
end

#read_specialObject

rubocop:enable Metrics/CyclomaticComplexity rubocop:enable Metrics/AbcSize



60
61
62
63
64
65
# File 'lib/clojure/reader.rb', line 60

def read_special
  case next_char
  when /_/ then read_sexp_comment
  else raise StandardError, "Unknown token: ##{cursor}"
  end
end

#read_stringObject

rubocop:disable Metrics/AbcSize



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/clojure/reader.rb', line 120

def read_string
  next_char
  if cursor == '"'
    next_char
    return ["str", ["quote", ""]]
  end
  s = cursor.dup
  prev = cursor
  until next_char == '"' && prev != "\\"
    if cursor != "\\"
      s << if prev == "\\" && cursor != '"'
             "\\#{cursor}"
           else
             cursor
           end
    end
    prev = cursor
  end
  next_char
  ["str", ["quote", s]]
end

#read_symbolObject

rubocop:enable Metrics/AbcSize



143
144
145
146
147
# File 'lib/clojure/reader.rb', line 143

def read_symbol
  symbol = cursor
  symbol << cursor while next_char.match(%r{\w|-|\.|\?|\+|/})
  symbol
end

#skip_charObject



73
74
75
76
# File 'lib/clojure/reader.rb', line 73

def skip_char
  next_char
  nil
end

#skip_commentObject



78
79
80
81
# File 'lib/clojure/reader.rb', line 78

def skip_comment
  next_char until cursor == "\n" || eof?
  nil
end