brotorift 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/brotorift +12 -0
- data/lib/ast.rb +169 -0
- data/lib/brotorift.rb +102 -0
- data/lib/case_helper.rb +13 -0
- data/lib/compiler.rb +288 -0
- data/lib/compiler_error.rb +191 -0
- data/lib/generators/elixir_server_generator.ex.erb +162 -0
- data/lib/generators/elixir_server_generator.rb +435 -0
- data/lib/generators/scala_server_generator.rb +137 -0
- data/lib/generators/scala_server_generator.scala.erb +92 -0
- data/lib/generators/unity_client_generator.cs.erb +103 -0
- data/lib/generators/unity_client_generator.rb +188 -0
- data/lib/lexer.rb +42 -0
- data/lib/parser.rb +105 -0
- data/lib/runtime.rb +345 -0
- data/lib/sequence_diagram_generator.rb +47 -0
- metadata +88 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ffeb983c09de3b7835c52798bfe30d658f6f035bd337a000051cbba4a5e0bc31
|
4
|
+
data.tar.gz: 6b963b0f2f00c5d167780b5f2c1d3a4ec63e8aa51b05e98cbd386a15678ce03e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ad9317a9da0207ebcbf9caa079447968115f87e59e71158c815a7f126e6e20f5369465b5ea2c3ff160c690b412f1ecd9184c99a26d91040acaff1f005f78807c
|
7
|
+
data.tar.gz: 595c9295e383cde70247d1f4c06f207a8b81b4ec08bbbf130a7d9456198edca8831c0304c25eb5672de88d5fc25eb4bf1355e3a9f427a5c9a65ea7725216c09f
|
data/bin/brotorift
ADDED
data/lib/ast.rb
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'rltk/ast'
|
2
|
+
require_relative 'lexer'
|
3
|
+
|
4
|
+
|
5
|
+
module RLTK
|
6
|
+
class StreamPosition
|
7
|
+
def to_s
|
8
|
+
"#{file_name}(#{line_number})"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
class Position
|
15
|
+
attr_reader :start, :stop
|
16
|
+
|
17
|
+
def initialize start, stop
|
18
|
+
raise "Filename should be the same." if start.file_name != stop.file_name
|
19
|
+
@start = start
|
20
|
+
@stop = stop
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
@start.to_s
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
class ASTNode < RLTK::ASTNode
|
30
|
+
value :position, Position
|
31
|
+
|
32
|
+
def doc_str
|
33
|
+
if doc == ''
|
34
|
+
return ''
|
35
|
+
else
|
36
|
+
return " # #{doc}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class TopDecl < ASTNode
|
42
|
+
end
|
43
|
+
|
44
|
+
class IncludeDecl < TopDecl
|
45
|
+
value :filename, String
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
"include '#{filename}'"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class NodeDecl < TopDecl
|
53
|
+
value :name, String
|
54
|
+
value :language, String
|
55
|
+
value :nickname, String
|
56
|
+
value :namespace, String
|
57
|
+
value :doc, String
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
nickname_str = ''
|
61
|
+
nickname_str = " as #{nickname}" if nickname != name
|
62
|
+
namespace_str = ''
|
63
|
+
namespace_str = " namespace #{namespace}" if namespace != ''
|
64
|
+
"node #{language} #{name}#{nickname_str}#{namespace_str}#{doc_str}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class TypeDecl < ASTNode
|
69
|
+
value :name, String
|
70
|
+
child :params, [TypeDecl]
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
params_str = ''
|
74
|
+
if params.length > 0
|
75
|
+
params_inner_str = params.map { |p| p.to_s } .join ','
|
76
|
+
params_str = '<' + params_inner_str + '>'
|
77
|
+
end
|
78
|
+
"#{name}#{params_str}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class MemberDecl < ASTNode
|
83
|
+
value :type, TypeDecl
|
84
|
+
value :name, String
|
85
|
+
value :doc, String
|
86
|
+
|
87
|
+
def to_s
|
88
|
+
"#{type} #{name}#{doc_str}\n"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class StructDecl < TopDecl
|
93
|
+
value :name, String
|
94
|
+
value :doc, String
|
95
|
+
child :members, [MemberDecl]
|
96
|
+
|
97
|
+
def to_s
|
98
|
+
members_str = members.join ''
|
99
|
+
"struct #{name}#{doc_str}\n#{members_str}end"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class EnumElementDecl < ASTNode
|
104
|
+
value :name, String
|
105
|
+
value :value, Integer
|
106
|
+
value :doc, String
|
107
|
+
|
108
|
+
def to_s
|
109
|
+
"#{name} = #{value}#{doc_str}\n"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class EnumDecl < TopDecl
|
114
|
+
value :name, String
|
115
|
+
value :doc, String
|
116
|
+
child :elements, [EnumElementDecl]
|
117
|
+
|
118
|
+
def to_s
|
119
|
+
elements_str = elements.join ''
|
120
|
+
"enum #{name}#{doc_str}\n#{elements_str}end"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class MessageDecl < ASTNode
|
125
|
+
value :name, String
|
126
|
+
value :doc, String
|
127
|
+
child :members, [MemberDecl]
|
128
|
+
|
129
|
+
def to_s
|
130
|
+
members_str = members.join ''
|
131
|
+
"message #{name}#{doc_str}\n#{members_str}end\n"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class DirectionDecl < TopDecl
|
136
|
+
value :client, String
|
137
|
+
value :direction, Symbol
|
138
|
+
value :server, String
|
139
|
+
value :doc, String
|
140
|
+
child :messages, [MessageDecl]
|
141
|
+
|
142
|
+
def to_s
|
143
|
+
messages_str = messages.join ''
|
144
|
+
"direction #{client} #{direction} #{server}#{doc_str}\n#{messages_str}end"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
class StepDecl < ASTNode
|
149
|
+
value :client, String
|
150
|
+
value :direction, Symbol
|
151
|
+
value :server, String
|
152
|
+
value :message, String
|
153
|
+
value :doc, String
|
154
|
+
|
155
|
+
def to_s
|
156
|
+
"#{client} #{direction} #{server}: #{message}#{doc_str}\n"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
class SequenceDecl < TopDecl
|
161
|
+
value :name, String
|
162
|
+
value :doc, String
|
163
|
+
child :steps, [StepDecl]
|
164
|
+
|
165
|
+
def to_s
|
166
|
+
steps_str = steps.join ''
|
167
|
+
"sequence #{name}#{doc_str}\n#{steps_str}end"
|
168
|
+
end
|
169
|
+
end
|
data/lib/brotorift.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
require_relative 'compiler'
|
3
|
+
require_relative 'sequence_diagram_generator'
|
4
|
+
|
5
|
+
|
6
|
+
class Generator
|
7
|
+
attr_reader :language, :side
|
8
|
+
|
9
|
+
@@generators = []
|
10
|
+
|
11
|
+
def initialize language, side
|
12
|
+
@language = language
|
13
|
+
@side = side
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.add generator
|
17
|
+
@@generators.push generator
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.generators
|
21
|
+
@@generators
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
class Brotorift
|
27
|
+
def initialize
|
28
|
+
@generated_nodes = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_generators
|
32
|
+
root_folder = File.expand_path File.dirname __FILE__
|
33
|
+
generators_pattern = root_folder + '/generators/*.rb'
|
34
|
+
Dir.glob generators_pattern do |generator_file|
|
35
|
+
require generator_file
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def print_compile_errors errors
|
41
|
+
errors.each do |e|
|
42
|
+
puts e
|
43
|
+
end
|
44
|
+
puts 'Your brotorift files contain errors. Please fix them before go on.'.red
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def generate_all_code runtime
|
49
|
+
runtime.directions.each do |direction|
|
50
|
+
generate_code runtime, direction.client, :client
|
51
|
+
generate_code runtime, direction.server, :server
|
52
|
+
end
|
53
|
+
|
54
|
+
generate_sequence_diagrams runtime
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def generate_sequence_diagrams runtime
|
59
|
+
begin
|
60
|
+
Dir.mkdir 'docs'
|
61
|
+
Dir.mkdir 'docs/diagrams'
|
62
|
+
rescue
|
63
|
+
end
|
64
|
+
SequenceDiagramGenerator.generate runtime
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def generate_code runtime, node, side
|
69
|
+
old_sides = @generated_nodes[node.name]
|
70
|
+
return if old_sides != nil and old_sides.include? side
|
71
|
+
|
72
|
+
generator = find_generator node.language, side
|
73
|
+
if generator == nil
|
74
|
+
puts "Generator for language '#{node.language}' and side #{side} is not found.".red
|
75
|
+
return
|
76
|
+
end
|
77
|
+
|
78
|
+
puts "Generating #{node.language} #{side} code for node '#{node.name}'..."
|
79
|
+
generator.generate node, runtime
|
80
|
+
if old_sides != nil
|
81
|
+
old_sides.push side
|
82
|
+
else
|
83
|
+
@generated_nodes[node.name] = [side]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def find_generator language, side
|
88
|
+
Generator.generators.find { |g| g.language == language and g.side == side }
|
89
|
+
end
|
90
|
+
|
91
|
+
def run file_name
|
92
|
+
load_generators
|
93
|
+
compiler = Compiler.new
|
94
|
+
compiler.compile file_name
|
95
|
+
if compiler.errors.length > 0
|
96
|
+
print_compile_errors compiler.errors
|
97
|
+
exit 1
|
98
|
+
else
|
99
|
+
generate_all_code compiler.runtime
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/case_helper.rb
ADDED
data/lib/compiler.rb
ADDED
@@ -0,0 +1,288 @@
|
|
1
|
+
require_relative 'parser'
|
2
|
+
require_relative 'compiler_error'
|
3
|
+
require_relative 'runtime'
|
4
|
+
|
5
|
+
|
6
|
+
class String
|
7
|
+
def char_case
|
8
|
+
if self == self.upcase
|
9
|
+
return :upper
|
10
|
+
else
|
11
|
+
return :lower
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
class Compiler
|
18
|
+
attr_reader :errors, :runtime
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@errors = []
|
22
|
+
@message_base_id = 0
|
23
|
+
@message_id = 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def compile filename
|
27
|
+
@runtime = self.compile_file filename
|
28
|
+
end
|
29
|
+
|
30
|
+
def compile_file filename
|
31
|
+
content = File.read filename, encoding: 'utf-8'
|
32
|
+
tokens = Lexer::lex content, filename
|
33
|
+
begin
|
34
|
+
ast = Parser::parse tokens
|
35
|
+
rescue RLTK::NotInLanguage => e
|
36
|
+
add_error UnexpectedTokenError.new e.current
|
37
|
+
return
|
38
|
+
end
|
39
|
+
runtime = Runtime.new filename
|
40
|
+
self.compile_ast runtime, ast
|
41
|
+
runtime
|
42
|
+
end
|
43
|
+
|
44
|
+
def compile_ast runtime, ast
|
45
|
+
@runtime = runtime
|
46
|
+
ast.each do |decl|
|
47
|
+
begin
|
48
|
+
case decl
|
49
|
+
when IncludeDecl
|
50
|
+
compile_include decl
|
51
|
+
when EnumDecl
|
52
|
+
compile_enum decl
|
53
|
+
when NodeDecl
|
54
|
+
compile_node decl
|
55
|
+
when StructDecl
|
56
|
+
compile_struct decl
|
57
|
+
when DirectionDecl
|
58
|
+
compile_direction decl
|
59
|
+
when SequenceDecl
|
60
|
+
compile_sequence decl
|
61
|
+
end
|
62
|
+
rescue CompilerError => e
|
63
|
+
@errors.push e
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def compile_include include_ast
|
69
|
+
filename = include_ast.filename + '.b'
|
70
|
+
if not File.exists? filename
|
71
|
+
add_error IncludeFileNotFoundError.new filename, include_ast.position
|
72
|
+
return
|
73
|
+
end
|
74
|
+
include_runtime = self.compile_file filename
|
75
|
+
@runtime.add_include include_runtime
|
76
|
+
end
|
77
|
+
|
78
|
+
def compile_enum enum_ast
|
79
|
+
self.check_case 'enum', :upper, enum_ast
|
80
|
+
self.check_type_unique enum_ast
|
81
|
+
enum_def = EnumTypeDef.new enum_ast
|
82
|
+
enum_ast.elements.each do |element_ast|
|
83
|
+
self.check_case 'enum element', :upper, element_ast
|
84
|
+
self.check_unique 'enum element', enum_def.elements[element_ast.name], element_ast
|
85
|
+
element_def = EnumElementDef.new element_ast
|
86
|
+
enum_def.add_element element_def
|
87
|
+
end
|
88
|
+
@runtime.add_enum enum_def
|
89
|
+
end
|
90
|
+
|
91
|
+
def compile_node node_ast
|
92
|
+
self.check_case 'node', :upper, node_ast
|
93
|
+
self.check_case 'node_nick', :upper, node_ast
|
94
|
+
self.check_unique 'node', @runtime.nodes[node_ast.name], node_ast
|
95
|
+
self.check_unique 'node', @runtime.nodes[node_ast.nickname], node_ast
|
96
|
+
node_def = NodeDef.new node_ast
|
97
|
+
@runtime.add_node node_def
|
98
|
+
end
|
99
|
+
|
100
|
+
def compile_struct struct_ast
|
101
|
+
self.check_case 'struct', :upper, struct_ast
|
102
|
+
self.check_type_unique struct_ast
|
103
|
+
struct_def = StructTypeDef.new struct_ast
|
104
|
+
struct_ast.members.each do |member_ast|
|
105
|
+
self.check_case 'struct member', :lower, member_ast
|
106
|
+
self.check_unique 'struct member', struct_def.get_member(member_ast.name), member_ast
|
107
|
+
member_type_def = self.get_member_type @runtime.nodes.values, member_ast.type
|
108
|
+
member_def = MemberDef.new member_ast, member_type_def
|
109
|
+
struct_def.add_member member_def
|
110
|
+
end
|
111
|
+
@runtime.add_struct struct_def
|
112
|
+
end
|
113
|
+
|
114
|
+
def compile_direction direction_ast
|
115
|
+
client = @runtime.nodes[direction_ast.client]
|
116
|
+
if client == nil
|
117
|
+
add_error NodeNotFoundError.new direction_ast, direction_ast.client
|
118
|
+
return
|
119
|
+
end
|
120
|
+
|
121
|
+
server = @runtime.nodes[direction_ast.server]
|
122
|
+
if server == nil
|
123
|
+
add_error NodeNotFoundError.new direction_ast, direction_ast.server
|
124
|
+
return
|
125
|
+
end
|
126
|
+
|
127
|
+
if client == server
|
128
|
+
add_error ClientServerSameError.new direction_ast
|
129
|
+
return
|
130
|
+
end
|
131
|
+
|
132
|
+
@message_base_id += 1000
|
133
|
+
@message_id = 0
|
134
|
+
|
135
|
+
old_direction = @runtime.get_direction client, direction_ast.direction, server
|
136
|
+
self.check_unique 'direction', old_direction, direction_ast
|
137
|
+
|
138
|
+
direction_def = DirectionDef.new direction_ast, client, server
|
139
|
+
direction_ast.messages.each do |message_ast|
|
140
|
+
self.check_case 'message', :upper, message_ast
|
141
|
+
self.check_unique 'message', direction_def.messages[message_ast.name], message_ast
|
142
|
+
direction_def.add_message self.compile_message direction_def, message_ast
|
143
|
+
end
|
144
|
+
@runtime.add_direction direction_def
|
145
|
+
end
|
146
|
+
|
147
|
+
def compile_message direction_def, message_ast
|
148
|
+
@message_id += 1
|
149
|
+
message_def = MessageDef.new message_ast, @message_base_id + @message_id
|
150
|
+
message_ast.members.each do |member_ast|
|
151
|
+
self.check_case 'message member', :lower, member_ast
|
152
|
+
self.check_unique 'message member', message_def.get_member(member_ast.name), member_ast
|
153
|
+
|
154
|
+
nodes_to_check = [direction_def.client, direction_def.server]
|
155
|
+
member_type_def = self.get_member_type nodes_to_check, member_ast.type
|
156
|
+
member_def = MemberDef.new member_ast, member_type_def
|
157
|
+
message_def.add_member member_def
|
158
|
+
end
|
159
|
+
message_def
|
160
|
+
end
|
161
|
+
|
162
|
+
def compile_sequence sequence_ast
|
163
|
+
self.check_case 'sequence', :upper, sequence_ast
|
164
|
+
self.check_unique 'sequence', @runtime.get_sequence(sequence_ast.name), sequence_ast
|
165
|
+
sequence_def = SequenceDef.new sequence_ast
|
166
|
+
sequence_ast.steps.each do |step_ast|
|
167
|
+
sequence_def.add_step self.compile_step step_ast
|
168
|
+
end
|
169
|
+
@runtime.add_sequence sequence_def
|
170
|
+
end
|
171
|
+
|
172
|
+
def compile_step step_ast
|
173
|
+
client = @runtime.nodes[step_ast.client]
|
174
|
+
if client == nil
|
175
|
+
add_error NodeNotFoundError.new step_ast, step_ast.client
|
176
|
+
return nil
|
177
|
+
end
|
178
|
+
|
179
|
+
server = @runtime.nodes[step_ast.server]
|
180
|
+
if server == nil
|
181
|
+
add_error NodeNotFoundError.new step_ast, step_ast.server
|
182
|
+
return nil
|
183
|
+
end
|
184
|
+
|
185
|
+
if client == server
|
186
|
+
add_error ClientServerSameError.new step_ast
|
187
|
+
return nil
|
188
|
+
end
|
189
|
+
|
190
|
+
direction_def = @runtime.get_direction client, step_ast.direction, server
|
191
|
+
if direction_def == nil
|
192
|
+
add_error DirectionNotFoundError.new step_ast, client, step_ast.direction, server
|
193
|
+
return nil
|
194
|
+
end
|
195
|
+
|
196
|
+
message_def = direction_def.messages[step_ast.message]
|
197
|
+
if message_def == nil
|
198
|
+
add_error MessageNotFoundError.new step_ast, direction_def
|
199
|
+
return nil
|
200
|
+
end
|
201
|
+
|
202
|
+
StepDef.new step_ast, direction_def, message_def
|
203
|
+
end
|
204
|
+
|
205
|
+
def get_member_type nodes_to_check, member_type_ast
|
206
|
+
type_def, runtime = self.get_type member_type_ast, true
|
207
|
+
return nil if type_def == nil
|
208
|
+
|
209
|
+
params = []
|
210
|
+
case type_def.name
|
211
|
+
when 'List'
|
212
|
+
self.check_type_param_count member_type_ast, 1
|
213
|
+
params.push self.get_member_type nodes_to_check, member_type_ast.params[0]
|
214
|
+
when 'Set'
|
215
|
+
self.check_type_param_count member_type_ast, 1
|
216
|
+
params.push self.get_member_type nodes_to_check, member_type_ast.params[0]
|
217
|
+
when 'Map'
|
218
|
+
self.check_type_param_count member_type_ast, 2
|
219
|
+
params.push self.get_member_type nodes_to_check, member_type_ast.params[0]
|
220
|
+
params.push self.get_member_type nodes_to_check, member_type_ast.params[1]
|
221
|
+
else
|
222
|
+
self.check_type_param_count member_type_ast, 0
|
223
|
+
end
|
224
|
+
|
225
|
+
member_type_def = TypeInstanceDef.new member_type_ast, type_def, params, runtime
|
226
|
+
self.check_type_nodes member_type_ast, member_type_def
|
227
|
+
member_type_def
|
228
|
+
end
|
229
|
+
|
230
|
+
def check_type_nodes member_type_ast, member_type_def
|
231
|
+
return if @runtime == member_type_def.runtime
|
232
|
+
|
233
|
+
@runtime.nodes.each do |n|
|
234
|
+
external_node = member_type_def.runtime.nodes.find { |node| n.name == node.name }
|
235
|
+
if external_node == nil
|
236
|
+
add_error CorrespondingNodeNotFoundError.new member_type_def, n.name
|
237
|
+
return
|
238
|
+
end
|
239
|
+
|
240
|
+
if external_node.language != n.language
|
241
|
+
add_error NodeLanguageMismatchError.new member_type_def, n, external_node
|
242
|
+
return
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def get_type ast, raise_error
|
248
|
+
return nil if ast == nil
|
249
|
+
|
250
|
+
type_def, runtime = @runtime.get_type ast.name
|
251
|
+
return type_def, runtime if type_def != nil
|
252
|
+
|
253
|
+
add_error TypeNotFoundError.new ast, ast.name if raise_error
|
254
|
+
return type_def, runtime
|
255
|
+
end
|
256
|
+
|
257
|
+
def check_case type, initial_case, ast
|
258
|
+
if type == 'node_nick' and initial_case != ast.nickname[0].char_case
|
259
|
+
add_error InitialCaseError.new type, initial_case, ast
|
260
|
+
elsif initial_case != ast.name[0].char_case
|
261
|
+
add_error InitialCaseError.new type, initial_case, ast
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def check_type_unique ast
|
266
|
+
type_def, _ = self.get_type ast, false
|
267
|
+
return if type_def == nil
|
268
|
+
|
269
|
+
if type_def.is_a? BuiltinTypeDef
|
270
|
+
add_error BuiltinNameConflictError.new ast
|
271
|
+
else
|
272
|
+
self.check_unique 'type', type_def, ast
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def check_unique type, old_def, new_ast
|
277
|
+
return if old_def == nil
|
278
|
+
add_error DuplicateDefError.new type, old_def.name, old_def.ast, new_ast
|
279
|
+
end
|
280
|
+
|
281
|
+
def check_type_param_count ast, expected_count
|
282
|
+
add_error TypeParamCountMismatchError.new ast, expected_count if ast.params.length != expected_count
|
283
|
+
end
|
284
|
+
|
285
|
+
def add_error error
|
286
|
+
@errors.push error
|
287
|
+
end
|
288
|
+
end
|