Class: Baba::Resolver

Inherits:
Object
  • Object
show all
Defined in:
lib/baba/resolver.rb

Defined Under Namespace

Classes: BreakableType, ClassType, FunctionType

Instance Method Summary collapse

Constructor Details

#initialize(interpreter) ⇒ Resolver

Returns a new instance of Resolver.



21
22
23
24
25
26
27
# File 'lib/baba/resolver.rb', line 21

def initialize(interpreter)
  @interpreter = interpreter
  @scopes = []
  @current_function = FunctionType::NONE
  @current_breakable = BreakableType::NONE
  @current_class = ClassType::NONE
end

Instance Method Details

#begin_scopeObject



52
53
54
# File 'lib/baba/resolver.rb', line 52

def begin_scope()
  @scopes << {}
end

#declare(name) ⇒ Object



60
61
62
63
64
65
66
67
68
# File 'lib/baba/resolver.rb', line 60

def declare(name)
  return if @scopes.empty?

  if @scopes.last.include?(name.lexeme)
    Baba.parser_error(name, "Already declared a variable with this name in this scope.")
  end

  @scopes.last[name.lexeme] = false
end

#define(name) ⇒ Object



70
71
72
73
# File 'lib/baba/resolver.rb', line 70

def define(name)
  return if @scopes.empty?
  @scopes.last[name.lexeme] = true
end

#end_scopeObject



56
57
58
# File 'lib/baba/resolver.rb', line 56

def end_scope
  @scopes.pop
end

#resolve(statements) ⇒ Object



29
30
31
32
33
34
35
# File 'lib/baba/resolver.rb', line 29

def resolve(statements)
  if statements.is_a?(Array)
    statements.each { |s| resolve(s) }
  else
    statements.accept(self)
  end
end

#resolve_function(function, type) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/baba/resolver.rb', line 37

def resolve_function(function, type)
  enclosing_function = @current_function
  @current_function = type

  begin_scope()
  function.params.each do |p|
    declare(p)
    define(p)
  end
  resolve(function.body)
  end_scope()

  @current_function = enclosing_function
end

#resolve_local(expr, name) ⇒ Object



75
76
77
78
79
80
81
82
83
# File 'lib/baba/resolver.rb', line 75

def resolve_local(expr, name)
  (0...(@scopes.size)).reverse_each do |i|
    scope = @scopes[i]
    if scope.include?(name.lexeme)
      @interpreter.resolve(expr, @scopes.size - 1 - i)
      break
    end
  end
end

#visit_assign_expr(expr) ⇒ Object



201
202
203
204
# File 'lib/baba/resolver.rb', line 201

def visit_assign_expr(expr)
  resolve(expr.value)
  resolve_local(expr, expr.name)
end

#visit_binary_expr(expr) ⇒ Object



206
207
208
209
# File 'lib/baba/resolver.rb', line 206

def visit_binary_expr(expr)
  resolve(expr.left)
  resolve(expr.right)
end

#visit_block_stmt(stmt) ⇒ Object



85
86
87
88
89
# File 'lib/baba/resolver.rb', line 85

def visit_block_stmt(stmt)
  begin_scope()
  resolve(stmt.statements)
  end_scope()
end

#visit_break_expr(expr) ⇒ Object



91
92
93
94
95
# File 'lib/baba/resolver.rb', line 91

def visit_break_expr(expr)
  if @current_breakable == BreakableType::NONE
    Baba.parser_error(stmt.keyword, "Can't break from a non-breakable statement.")
  end
end

#visit_call_expr(expr) ⇒ Object



211
212
213
214
# File 'lib/baba/resolver.rb', line 211

def visit_call_expr(expr)
  resolve(expr.callee)
  expr.arguments.each { |a| resolve(a) }
end

#visit_class_stmt(stmt) ⇒ Object



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
130
131
132
133
134
# File 'lib/baba/resolver.rb', line 97

def visit_class_stmt(stmt)
  enclosing_class = @current_class
  @current_class = ClassType::CLASS

  declare(stmt.name)
  define(stmt.name)

  if !stmt.superclass.nil? && stmt.superclass.name.lexeme == stmt.name.lexeme
    Baba.parser_error(stmt.superclass.name, "A thing cannot inherit from itself.")
  end

  unless stmt.superclass.nil?
    @current_class = ClassType::SUBCLASS
    resolve(stmt.superclass)

    begin_scope()
    @scopes.last["super"] = true
  end

  begin_scope()
  @scopes.last["self"] = true

  stmt.methods.each do |method|
    declaration = FunctionType::METHOD
    if method.name.lexeme == "init"
      declaration = FunctionType::INIT
    end
    resolve_function(method, declaration)
  end

  end_scope()

  unless stmt.superclass.nil?
    end_scope()
  end

  @current_class = enclosing_class
end

#visit_expression_stmt(stmt) ⇒ Object



136
137
138
# File 'lib/baba/resolver.rb', line 136

def visit_expression_stmt(stmt)
  resolve(stmt.expression)
end

#visit_function_stmt(stmt) ⇒ Object



178
179
180
181
182
183
# File 'lib/baba/resolver.rb', line 178

def visit_function_stmt(stmt)
  declare(stmt.name)
  define(stmt.name)

  resolve_function(stmt, FunctionType::FUNCTION)
end

#visit_get_expr(expr) ⇒ Object



216
217
218
# File 'lib/baba/resolver.rb', line 216

def visit_get_expr(expr)
  resolve(expr.object)
end

#visit_grouping_expr(expr) ⇒ Object



220
221
222
# File 'lib/baba/resolver.rb', line 220

def visit_grouping_expr(expr)
  resolve(expr.expression)
end

#visit_if_stmt(stmt) ⇒ Object



140
141
142
143
144
# File 'lib/baba/resolver.rb', line 140

def visit_if_stmt(stmt)
  resolve(stmt.condition)
  resolve(stmt.then_branch)
  resolve stmt.else_branch unless stmt.else_branch.nil?
end

#visit_include_stmt(stmt) ⇒ Object



146
147
148
# File 'lib/baba/resolver.rb', line 146

def visit_include_stmt(stmt)
  resolve(stmt.expression)
end

#visit_literal_expr(expr) ⇒ Object



224
225
# File 'lib/baba/resolver.rb', line 224

def visit_literal_expr(expr)
end

#visit_logical_expr(expr) ⇒ Object



227
228
229
230
# File 'lib/baba/resolver.rb', line 227

def visit_logical_expr(expr)
  resolve(expr.left)
  resolve(expr.right)
end

#visit_rbeval_stmt(stmt) ⇒ Object



174
175
176
# File 'lib/baba/resolver.rb', line 174

def visit_rbeval_stmt(stmt)
  resolve(stmt.expression)
end

#visit_return_stmt(stmt) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/baba/resolver.rb', line 160

def visit_return_stmt(stmt)
  if @current_function == FunctionType::NONE
    Baba.parser_error(stmt.keyword, "Can't return from top-level code.")
  end

  unless stmt.value.nil?
    if @current_function == FunctionType::INIT && !stmt.value.is_a?(Expr::Self)
      Baba.parser_error(stmt.keyword, "Can't return a non-self value from an initializer.")
    end

    resolve(stmt.value)
  end
end

#visit_self_expr(expr) ⇒ Object



232
233
234
235
236
237
238
# File 'lib/baba/resolver.rb', line 232

def visit_self_expr(expr)
  if @current_class == ClassType::NONE
    Baba.parser_error(expr.keyword, "Can't use 'self' outside of a thing.")
  end

  resolve_local(expr, expr.keyword)
end

#visit_set_expr(expr) ⇒ Object



240
241
242
243
# File 'lib/baba/resolver.rb', line 240

def visit_set_expr(expr)
  resolve(expr.value)
  resolve(expr.object)
end

#visit_super_expr(expr) ⇒ Object



245
246
247
248
249
250
251
252
253
# File 'lib/baba/resolver.rb', line 245

def visit_super_expr(expr)
  if @current_class == ClassType::NONE
    Baba.parser_error(expr.keyword, "Can't use 'super' outside of a thing.")
  elsif @current_class == ClassType::CLASS
    Baba.parser_error(expr.keyword, "Can't use 'super' in a thing with no super thing.")
  end

  resolve_local(expr, expr.keyword)
end

#visit_unary_expr(expr) ⇒ Object



255
256
257
# File 'lib/baba/resolver.rb', line 255

def visit_unary_expr(expr)
  resolve(expr.right)
end

#visit_var_stmt(stmt) ⇒ Object



185
186
187
188
189
190
191
# File 'lib/baba/resolver.rb', line 185

def visit_var_stmt(stmt)
  declare(stmt.name)
  unless stmt.initializer.nil?
    resolve(stmt.initializer)
  end
  define(stmt.name)
end

#visit_variable_expr(expr) ⇒ Object



193
194
195
196
197
198
199
# File 'lib/baba/resolver.rb', line 193

def visit_variable_expr(expr)
  if !@scopes.empty? && @scopes.last[expr.name.lexeme] == false
    Baba.parser_error(expr.name, "Can't read local variable in its own initializer.")
  end

  resolve_local(expr, expr.name)
end

#visit_while_stmt(stmt) ⇒ Object



150
151
152
153
154
155
156
157
158
# File 'lib/baba/resolver.rb', line 150

def visit_while_stmt(stmt)
  enclosing_break = @current_breakable
  @current_breakable = BreakableType::WHILE

  resolve(stmt.condition)
  resolve(stmt.body)

  @current_breakable = enclosing_break
end