brotorift 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ARGV.length < 1
4
+ puts 'Usage: brotorift [file_to_compile.brotorift]'
5
+ puts ' Please provide the brotorift file.'
6
+ puts
7
+ exit 1
8
+ end
9
+
10
+ require 'brotorift'
11
+ brotorift = Brotorift.new
12
+ brotorift.run ARGV[0]
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
@@ -0,0 +1,13 @@
1
+ class String
2
+ def decapitalize
3
+ self[0].downcase + self[1..-1]
4
+ end
5
+
6
+ def underscore
7
+ self.gsub(/::/, '/').
8
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
9
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
10
+ tr("-", "_").
11
+ downcase
12
+ end
13
+ end
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