summaryrefslogtreecommitdiff
path: root/lib/sexp/parse.rb
blob: 51a86e3f7c8d228019870478726a6fce3509e89c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
class Atom
  def initialize(content = nil)
    @content = content
  end

  attr_reader :content

  def to_s
    content
  end

  def self.from_token(token)
    new(token.content)
  end
end

class SExp
  def initialize(addr = nil, decr = nil)
    @addr = addr
    @decr = decr
  end

  def car
    @addr
  end
  def car=(addr)
    @addr = addr
  end

  def cdr
    @decr
  end
  def cdr=(decr)
    @decr = decr
  end

  def nil?
    car.nil? && cdr.nil?
  end

  def to_s
    if car.nil?
      "()"
    elsif cdr.nil?
      "(#{car} . ())"
    else
      "(#{car} . #{cdr})"
    end
  end

  def to_a
    res = []
    at = self

    until at.nil?
      res << at.car
      at = at.cdr
    end

    res
  end

  def self.from_a(list)
    if list.empty?
      SExp.new
    else
      SExp.new(list[0], SExp.from_a(list[1..-1]))
    end
  end
end

def peek_token(tokens)
  tokens.first
end

def get_token(tokens)
  tokens.shift
end

def num_members(tokens)
  members = 0
  level = 1
  at = 1

  until level == 0
    case tokens.at(at)
    when T_LParen
      members += 1 if level == 1
      level += 1
    when T_RParen
      level -= 1
    when T_Atom
      members += 1 if level == 1
    end
    at += 1
  end

  members
end

def parse_sexp(tokens)
  need = num_members(tokens)
  get_token(tokens)
  
  members = []

  need.times do
    case peek_token(tokens)
    when T_Atom
      members << Atom.from_token(get_token(tokens))
    when T_LParen
      members << parse_sexp(tokens)
    end
  end

  get_token(tokens)

  SExp.from_a(members)
end

def parse_sexps(tokens)
  sexps = []

  until tokens.empty?
    sexps << parse_sexp(tokens)
  end

  sexps
end