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.
@@ -0,0 +1,191 @@
1
+
2
+ class CompilerError
3
+ def initialize position
4
+ @position = position
5
+ end
6
+
7
+ def to_s
8
+ "#{@position}: #{info}"
9
+ end
10
+ end
11
+
12
+
13
+ class InitialCaseError < CompilerError
14
+ def initialize type, initial_case, ast
15
+ super ast.position
16
+ @type = type
17
+ @initial_case = initial_case
18
+ @ast = ast
19
+ end
20
+
21
+ def info
22
+ if @type == 'node_nick' then
23
+ return "'#{@ast.nickname}' : nickname of nodes should be #{@initial_case} case"
24
+ else
25
+ return "'#{@ast.name}' : name of #{@type}s should be #{@initial_case} case"
26
+ end
27
+ end
28
+ end
29
+
30
+
31
+ class BuiltinNameConflictError < CompilerError
32
+ def initialize ast
33
+ super ast.position
34
+ @name = ast.name
35
+ end
36
+
37
+ def info
38
+ "'#{@name}' : type name conflict with built-in type"
39
+ end
40
+ end
41
+
42
+
43
+ class DuplicateDefError < CompilerError
44
+ def initialize type, name, old_ast, new_ast
45
+ super new_ast.position
46
+ @type = type
47
+ @name = name
48
+ @old_pos = old_ast.position
49
+ @new_pos = new_ast.position
50
+ end
51
+
52
+ def info
53
+ "'#{@name}' : #{@type} redefinition\n #{@old_pos} : see previous #{@type} definition of '#{@name}'"
54
+ end
55
+ end
56
+
57
+
58
+ class IncludeFileNotFoundError < CompilerError
59
+ def initialize filename, position
60
+ super position
61
+ @filename = filename
62
+ end
63
+
64
+ def info
65
+ "'#{@filename}' : include file not found: "
66
+ end
67
+ end
68
+
69
+
70
+ class TypeParamCountMismatchError < CompilerError
71
+ def initialize ast, expected_count
72
+ super ast.position
73
+ @expected_count = expected_count
74
+ @actual_count = ast.params.length
75
+ end
76
+
77
+ def info
78
+ "generic parameter count mismatch: expected #{@expected_count}, provided #{@actual_count}"
79
+ end
80
+ end
81
+
82
+
83
+ class TypeNotFoundError < CompilerError
84
+ def initialize ast, name
85
+ super ast.position
86
+ @name = name
87
+ end
88
+
89
+ def info
90
+ "'#{@name}' : undefined type"
91
+ end
92
+ end
93
+
94
+
95
+ class NodeNotFoundError < CompilerError
96
+ def initialize ast, name
97
+ super ast.position
98
+ @name = name
99
+ end
100
+
101
+ def info
102
+ "'#{@name}' : undefined node"
103
+ end
104
+ end
105
+
106
+
107
+ class CorrespondingNodeNotFoundError < CompilerError
108
+ def initialize member_type_def, node_name
109
+ super member_type_def.ast.position
110
+ @type = member_type_def.type
111
+ @node_name = node_name
112
+ end
113
+
114
+ def info
115
+ "'#{@type.name}' : cannot find corresponding node '#{@node_name}'\n #{@type.ast.position} : see type definition of '#{@type.name}'"
116
+ end
117
+ end
118
+
119
+
120
+ class NodeLanguageMismatchError < CompilerError
121
+ def initialize member_type_def, node_def, external_node_def
122
+ super member_type_def.ast.position
123
+ @type = member_type_def.type
124
+ @node_name = node_def.name
125
+ @node_position = node_def.ast.position
126
+ @external_node_position = external_node_def.ast.position
127
+ end
128
+
129
+ def info
130
+ "'#{@type.name}' : node language mismatch\n" +
131
+ " #{@node_position} : see node definition of '#{@node_name}'\n" +
132
+ " #{@external_node_position} : see external node definition of '#{@node_name}'\n" +
133
+ " #{@type.ast.position} : see type definition of '#{@type.name}'\n"
134
+ end
135
+ end
136
+
137
+
138
+ class DirectionNotFoundError < CompilerError
139
+ def initialize ast, client, direction, server
140
+ super ast.position
141
+ @client = client
142
+ @direction = direction
143
+ @server = server
144
+ end
145
+
146
+ def info
147
+ case @direction
148
+ when :left
149
+ "'#{@client.name} -> #{@server.name}' : undefined direction"
150
+ when :right
151
+ "'#{@client.name} <- #{@server.name}' : undefined direction"
152
+ end
153
+ end
154
+ end
155
+
156
+
157
+ class MessageNotFoundError < CompilerError
158
+ def initialize ast, direction
159
+ super ast.position
160
+ @name = ast.message
161
+ @direction = direction
162
+ end
163
+
164
+ def info
165
+ "'#{@name}' undefined message in direction '#{@direction.name}'\n #{@direction.ast.position} : see previous direction definition of '#{@direction.name}'"
166
+ end
167
+ end
168
+
169
+
170
+ class ClientServerSameError < CompilerError
171
+ def initialize ast
172
+ super ast.position
173
+ end
174
+
175
+ def info
176
+ "client and server of a direction should not be the same"
177
+ end
178
+ end
179
+
180
+
181
+ class UnexpectedTokenError < CompilerError
182
+ def initialize token
183
+ super token.position
184
+ @type = token.type
185
+ @value = token.value
186
+ end
187
+
188
+ def info
189
+ "unexpected #{@type.to_s.downcase} : '#{@value}'"
190
+ end
191
+ end
@@ -0,0 +1,162 @@
1
+ defmodule <%= node.namespace %>.Enums do<% for e in runtime.enums.values %>
2
+ def <%= e.elixir_read_name %>(data) do
3
+ <<value::32-little, data::binary>> = data
4
+ case value do<% for v in e.elements.values %>
5
+ <%= v.value %> -> {data, <%= v.elixir_name %>}<% end %>
6
+ _ -> :error
7
+ end
8
+ end
9
+
10
+ def <%= e.elixir_write_name %>(data, value) do
11
+ case value do<% for v in e.elements.values %>
12
+ <%= v.elixir_name %> -> <<data::binary, <%= v.value %>::32-little>><% end %>
13
+ _ -> :error
14
+ end
15
+ end
16
+ <% end %>end
17
+
18
+ <% for s in runtime.structs.values %>
19
+ defmodule <%= node.namespace %>.<%= s.name %> do
20
+ defstruct [<%= s.elixir_members %>]
21
+
22
+ @typedoc """
23
+ <%= s.doc %><% for m in s.members %>
24
+ `:<%= m.elixir_name %>`: <%= m.doc %><% end %>
25
+ """
26
+ @type t :: %<%= node.namespace %>.<%= s.name %>{<%= s.elixir_members_with_types node %>}
27
+
28
+ def read(data) do<% for m in s.members %>
29
+ {data, <%= m.elixir_name %>} = <%= m.type.elixir_read node %><% end %>
30
+ {data, %{<%= s.elixir_members_with_values %>}}
31
+ end
32
+
33
+ def write(data, value) do<% for m in s.members %>
34
+ data = <%= m.type.elixir_write node, 'value.' + m.elixir_name %><% end %>
35
+ data
36
+ end
37
+ end
38
+ <% end %>
39
+ <% for n in runtime.get_node_directions node, :server %>
40
+ defmodule <%= node.namespace %>.<%= node.elixir_connection %> do
41
+ use GenServer, restart: :temporary
42
+
43
+ import <%= node.namespace %>.Enums
44
+
45
+ @behaviour Brotorift.ConnectionBehaviour
46
+ <% for m in n.in_direction.messages.values %>
47
+ <%= m.elixir_header_name %> <%= m.id %><% end %>
48
+ <% for m in n.out_direction.messages.values %>
49
+ <%= m.elixir_header_name %> <%= m.id %><% end %>
50
+ <% for m in n.out_direction.messages.values %>
51
+ @doc """
52
+ <%= m.doc %>
53
+
54
+ ## Parameters
55
+
56
+ - `connection`: <%= node.elixir_connection %> Pid<% for p in m.members %>
57
+ - `<%= p.elixir_name %>`: <%= p.doc %>
58
+ <% end %>
59
+ """
60
+ @spec <%= m.elixir_name %>(connection :: pid()<%= m.elixir_params_with_types node %>) :: :ok
61
+ def <%= m.elixir_name %>(connection<%= m.elixir_params %>) do
62
+ GenServer.cast(connection, {:<%= m.elixir_name %><%= m.elixir_params %>})
63
+ end
64
+ <% end %>
65
+ def start_link(args) do
66
+ GenServer.start_link(__MODULE__, args)
67
+ end
68
+
69
+ def handle_data(pid, data) do
70
+ GenServer.cast(pid, {:handle_data, data})
71
+ end
72
+
73
+ def stop(pid) do
74
+ GenServer.stop(pid)
75
+ end
76
+
77
+ def init({socket, transport, handler}) do
78
+ {:ok, state} = handler.open_connection(self(), socket)
79
+ {:ok, {socket, transport, handler, state}}
80
+ end
81
+
82
+ def handle_cast({:handle_data, data}, {socket, transport, handler, state}) do
83
+ {:ok, new_state} = process_packet(data, handler, state)
84
+ {:noreply, {socket, transport, handler, new_state}}
85
+ end
86
+ <% for m in n.out_direction.messages.values %>
87
+ def handle_cast({:<%= m.elixir_name %><%= m.elixir_params %>}, {socket, transport, handler, state}) do
88
+ data = <<<%= m.elixir_header_name %>::32-little>><% for p in m.members %>
89
+ data = <%= p.type.elixir_write node, p.elixir_name %><% end %>
90
+ data = <<byte_size(data)::32-little, data::binary>>
91
+ transport.send(socket, data)
92
+ {:noreply, {socket, transport, handler, state}}
93
+ end
94
+ <% end %>
95
+ def terminate(_reason, {_socket, _transport, handler, state}) do
96
+ handler.close_connection(self(), state)
97
+ end
98
+
99
+ defp process_packet(<<>>, _handler, state) do
100
+ {:ok, state}
101
+ end
102
+ defp process_packet(data, handler, state) do
103
+ <<_size::32-little, header::32-little, data::binary>> = data
104
+ case header do<% for m in n.in_direction.messages.values %>
105
+ <%= m.elixir_header_name %> -><% for p in m.members %>
106
+ {data, <%= p.elixir_name %>} = <%= p.type.elixir_read node %><% end %>
107
+ {:ok, state} = handler.<%= m.elixir_name %>(self(), state<%= m.elixir_params %>)
108
+ process_packet(data, handler, state)<% end %>
109
+ end
110
+ end
111
+ end
112
+
113
+
114
+ defmodule <%= node.namespace %>.<%= node.elixir_behaviour %> do
115
+
116
+ @doc """
117
+ Calls when the server started
118
+ """
119
+ @callback start() :: :ok
120
+
121
+ @doc """
122
+ Calls when a new client connects
123
+
124
+ ## Parameters
125
+
126
+ - `connection`: The <%= node.elixir_connection %> Pid for the client
127
+ - `socket`: The socket for the client
128
+
129
+ ## Returns
130
+
131
+ {:ok, state}
132
+
133
+ """
134
+ @callback open_connection(connection :: pid(), socket :: :gen_tcp.socket()) :: {:ok, any()}
135
+
136
+ @doc """
137
+ Calls when a client disconnects
138
+
139
+ ## Parameters
140
+
141
+ - `connection`: The <%= node.elixir_connection %> Pid for the client
142
+ - `state`: The state for the connection
143
+
144
+ """
145
+ @callback close_connection(connection :: pid(), state :: any()) :: :ok
146
+
147
+ <% for m in n.in_direction.messages.values %>
148
+ @doc """
149
+ <%= m.doc %>
150
+
151
+ ## Parameters
152
+
153
+ - `connection`: The <%= node.elixir_connection %> Pid for the client
154
+ - `state`: The state for the connection<% for p in m.members %>
155
+ - `<%= p.elixir_name %>`: <%= p.doc %>
156
+ <% end %>
157
+ """
158
+ @callback <%= m.elixir_name %>(connection :: pid(), state :: any()<%= m.elixir_params_with_types node %>) :: {:ok, any()}
159
+ <% end %>
160
+ end
161
+
162
+ <% end %>