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 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