Class: Compiler

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCompiler

Returns a new instance of Compiler.



20
21
22
23
24
# File 'lib/compiler.rb', line 20

def initialize
  @errors = []
  @message_base_id = 0
  @message_id = 0
end

Instance Attribute Details

#errorsObject (readonly)

Returns the value of attribute errors.



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

def errors
  @errors
end

#runtimeObject (readonly)

Returns the value of attribute runtime.



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

def runtime
  @runtime
end

Instance Method Details

#add_error(error) ⇒ Object



285
286
287
# File 'lib/compiler.rb', line 285

def add_error error
  @errors.push error
end

#check_case(type, initial_case, ast) ⇒ Object



257
258
259
260
261
262
263
# File 'lib/compiler.rb', line 257

def check_case type, initial_case, ast
  if type == 'node_nick' and initial_case != ast.nickname[0].char_case
    add_error InitialCaseError.new type, initial_case, ast
  elsif initial_case != ast.name[0].char_case
    add_error InitialCaseError.new type, initial_case, ast
  end
end

#check_type_nodes(member_type_ast, member_type_def) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/compiler.rb', line 230

def check_type_nodes member_type_ast, member_type_def
  return if @runtime == member_type_def.runtime

  @runtime.nodes.each do |n|
    external_node = member_type_def.runtime.nodes.find { |node| n.name == node.name }
    if external_node == nil
      add_error CorrespondingNodeNotFoundError.new member_type_def, n.name
      return
    end

    if external_node.language != n.language
      add_error NodeLanguageMismatchError.new member_type_def, n, external_node
      return
    end
  end
end

#check_type_param_count(ast, expected_count) ⇒ Object



281
282
283
# File 'lib/compiler.rb', line 281

def check_type_param_count ast, expected_count
  add_error TypeParamCountMismatchError.new ast, expected_count if ast.params.length != expected_count
end

#check_type_unique(ast) ⇒ Object



265
266
267
268
269
270
271
272
273
274
# File 'lib/compiler.rb', line 265

def check_type_unique ast
  type_def, _ = self.get_type ast, false
  return if type_def == nil

  if type_def.is_a? BuiltinTypeDef
    add_error BuiltinNameConflictError.new ast
  else
    self.check_unique 'type', type_def, ast
  end
end

#check_unique(type, old_def, new_ast) ⇒ Object



276
277
278
279
# File 'lib/compiler.rb', line 276

def check_unique type, old_def, new_ast
  return if old_def == nil
  add_error DuplicateDefError.new type, old_def.name, old_def.ast, new_ast
end

#compile(filename) ⇒ Object



26
27
28
# File 'lib/compiler.rb', line 26

def compile filename
  @runtime = self.compile_file filename
end

#compile_ast(runtime, ast) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/compiler.rb', line 44

def compile_ast runtime, ast
  @runtime = runtime
  ast.each do |decl|
    begin
      case decl
      when IncludeDecl
        compile_include decl
      when EnumDecl
        compile_enum decl
      when NodeDecl
        compile_node decl
      when StructDecl
        compile_struct decl
      when DirectionDecl
        compile_direction decl
      when SequenceDecl
        compile_sequence decl
      end
    rescue CompilerError => e
      @errors.push e
    end
  end
end

#compile_direction(direction_ast) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/compiler.rb', line 114

def compile_direction direction_ast
  client = @runtime.nodes[direction_ast.client]
  if client == nil
    add_error NodeNotFoundError.new direction_ast, direction_ast.client
    return
  end

  server = @runtime.nodes[direction_ast.server]
  if server == nil
    add_error NodeNotFoundError.new direction_ast, direction_ast.server
    return
  end

  if client == server
    add_error ClientServerSameError.new direction_ast
    return
  end

  @message_base_id += 1000
  @message_id = 0

  old_direction = @runtime.get_direction client, direction_ast.direction, server
  self.check_unique 'direction', old_direction, direction_ast

  direction_def = DirectionDef.new direction_ast, client, server
  direction_ast.messages.each do |message_ast|
    self.check_case 'message', :upper, message_ast
    self.check_unique 'message', direction_def.messages[message_ast.name], message_ast
    direction_def.add_message self.compile_message direction_def, message_ast
  end
  @runtime.add_direction direction_def
end

#compile_enum(enum_ast) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/compiler.rb', line 78

def compile_enum enum_ast
  self.check_case 'enum', :upper, enum_ast
  self.check_type_unique enum_ast
  enum_def = EnumTypeDef.new enum_ast
  enum_ast.elements.each do |element_ast|
    self.check_case 'enum element', :upper, element_ast
    self.check_unique 'enum element', enum_def.elements[element_ast.name], element_ast
    element_def = EnumElementDef.new element_ast
    enum_def.add_element element_def
  end
  @runtime.add_enum enum_def
end

#compile_file(filename) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/compiler.rb', line 30

def compile_file filename
  content = File.read filename, encoding: 'utf-8'
  tokens = Lexer::lex content, filename
  begin
    ast = Parser::parse tokens
  rescue RLTK::NotInLanguage => e
    add_error UnexpectedTokenError.new e.current
    return
  end
  runtime = Runtime.new filename
  self.compile_ast runtime, ast
  runtime
end

#compile_include(include_ast) ⇒ Object



68
69
70
71
72
73
74
75
76
# File 'lib/compiler.rb', line 68

def compile_include include_ast
  filename = include_ast.filename + '.b'
  if not File.exists? filename
    add_error IncludeFileNotFoundError.new filename, include_ast.position
    return
  end
  include_runtime = self.compile_file filename
  @runtime.add_include include_runtime
end

#compile_message(direction_def, message_ast) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/compiler.rb', line 147

def compile_message direction_def, message_ast
  @message_id += 1
  message_def = MessageDef.new message_ast, @message_base_id + @message_id
  message_ast.members.each do |member_ast|
    self.check_case 'message member', :lower, member_ast
    self.check_unique 'message member', message_def.get_member(member_ast.name), member_ast

    nodes_to_check = [direction_def.client, direction_def.server]
    member_type_def = self.get_member_type nodes_to_check, member_ast.type
    member_def = MemberDef.new member_ast, member_type_def
    message_def.add_member member_def
  end
  message_def
end

#compile_node(node_ast) ⇒ Object



91
92
93
94
95
96
97
98
# File 'lib/compiler.rb', line 91

def compile_node node_ast
  self.check_case 'node', :upper, node_ast
  self.check_case 'node_nick', :upper, node_ast
  self.check_unique 'node', @runtime.nodes[node_ast.name], node_ast
  self.check_unique 'node', @runtime.nodes[node_ast.nickname], node_ast
  node_def = NodeDef.new node_ast
  @runtime.add_node node_def
end

#compile_sequence(sequence_ast) ⇒ Object



162
163
164
165
166
167
168
169
170
# File 'lib/compiler.rb', line 162

def compile_sequence sequence_ast
  self.check_case 'sequence', :upper, sequence_ast
  self.check_unique 'sequence', @runtime.get_sequence(sequence_ast.name), sequence_ast
  sequence_def = SequenceDef.new sequence_ast
  sequence_ast.steps.each do |step_ast|
    sequence_def.add_step self.compile_step step_ast
  end
  @runtime.add_sequence sequence_def
end

#compile_step(step_ast) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/compiler.rb', line 172

def compile_step step_ast
  client = @runtime.nodes[step_ast.client]
  if client == nil
    add_error NodeNotFoundError.new step_ast, step_ast.client
    return nil
  end

  server = @runtime.nodes[step_ast.server]
  if server == nil
    add_error NodeNotFoundError.new step_ast, step_ast.server
    return nil
  end

  if client == server
    add_error ClientServerSameError.new step_ast
    return nil
  end

  direction_def = @runtime.get_direction client, step_ast.direction, server
  if direction_def == nil
    add_error DirectionNotFoundError.new step_ast, client, step_ast.direction, server
    return nil
  end

  message_def = direction_def.messages[step_ast.message]
  if message_def == nil
    add_error MessageNotFoundError.new step_ast, direction_def
    return nil
  end

  StepDef.new step_ast, direction_def, message_def
end

#compile_struct(struct_ast) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/compiler.rb', line 100

def compile_struct struct_ast
  self.check_case 'struct', :upper, struct_ast
  self.check_type_unique struct_ast
  struct_def = StructTypeDef.new struct_ast
  struct_ast.members.each do |member_ast|
    self.check_case 'struct member', :lower, member_ast
    self.check_unique 'struct member', struct_def.get_member(member_ast.name), member_ast
    member_type_def = self.get_member_type @runtime.nodes.values, member_ast.type
    member_def = MemberDef.new member_ast, member_type_def
    struct_def.add_member member_def
  end
  @runtime.add_struct struct_def
end

#get_member_type(nodes_to_check, member_type_ast) ⇒ Object



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/compiler.rb', line 205

def get_member_type nodes_to_check, member_type_ast
  type_def, runtime = self.get_type member_type_ast, true
  return nil if type_def == nil

  params = []
  case type_def.name
  when 'List'
    self.check_type_param_count member_type_ast, 1
    params.push self.get_member_type nodes_to_check, member_type_ast.params[0]
  when 'Set'
    self.check_type_param_count member_type_ast, 1
    params.push self.get_member_type nodes_to_check, member_type_ast.params[0]
  when 'Map'
    self.check_type_param_count member_type_ast, 2
    params.push self.get_member_type nodes_to_check, member_type_ast.params[0]
    params.push self.get_member_type nodes_to_check, member_type_ast.params[1]
  else
    self.check_type_param_count member_type_ast, 0
  end

  member_type_def = TypeInstanceDef.new member_type_ast, type_def, params, runtime
  self.check_type_nodes member_type_ast, member_type_def
  member_type_def
end

#get_type(ast, raise_error) ⇒ Object



247
248
249
250
251
252
253
254
255
# File 'lib/compiler.rb', line 247

def get_type ast, raise_error
  return nil if ast == nil
  
  type_def, runtime = @runtime.get_type ast.name
  return type_def, runtime if type_def != nil

  add_error TypeNotFoundError.new ast, ast.name if raise_error
  return type_def, runtime
end