1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
lpeg = require('lpeg')
msgpack = require('cmsgpack')
P, R, S = lpeg.P, lpeg.R, lpeg.S
C, Ct, Cc, Cg = lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg
any = P(1) -- (consume one character)
letter = R('az', 'AZ') + S('_$')
alpha = letter + R('09')
nl = P('\n')
not_nl = any - nl
ws = S(' \t') + nl
fill = ws ^ 0
c_comment = P('//') * (not_nl ^ 0)
c_preproc = P('#') * (not_nl ^ 0)
c_id = letter * (alpha ^ 0)
c_void = P('void')
c_raw = P('char') * fill * P('*')
c_int = P('uint32_t')
c_array = c_raw * fill * P('*') * Cc('array')
c_param_type = (
(c_array * Cc('array') * fill) +
(c_raw * Cc('raw') * fill) +
(c_int * Cc('integer') * (ws ^ 1))
)
c_type = (c_void * Cc('none') * (ws ^ 1)) + c_param_type
c_param = Ct(c_param_type * C(c_id))
c_param_list = c_param * (fill * (P(',') * fill * c_param) ^ 0)
c_params = Ct(c_void + c_param_list)
c_proto = Ct(
Cg(c_type, 'rtype') * Cg(c_id, 'fname') *
fill * P('(') * fill * Cg(c_params, 'params') * fill * P(')') *
fill * P(';')
)
grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1)
inputf = assert(arg[1])
outputf = assert(arg[2])
input = io.open(inputf, 'rb')
api = grammar:match(input:read('*all'))
input:close()
-- assign a unique integer id for each api function
for i = 1, #api do
api[i].id = i
end
output = io.open(outputf, 'wb')
output:write([[
#include <stdbool.h>
#include <stdint.h>
#include <msgpack.h>
#include "api.h"
#include "msgpack_rpc.h"
static const uint8_t msgpack_metadata[] = {
]])
packed = msgpack.pack(api)
for i = 1, #packed do
output:write(string.byte(packed, i)..', ')
if i % 10 == 0 then
output:write('\n ')
end
end
output:write([[
};
bool msgpack_rpc_dispatch(msgpack_object *req, msgpack_packer *res)
{
uint32_t method_id = (uint32_t)req->via.u64;
switch (method_id) {
case 0:
msgpack_rpc_response(req, res);
msgpack_pack_nil(res);
// The result is the `msgpack_metadata` byte array
msgpack_pack_raw(res, sizeof(msgpack_metadata));
msgpack_pack_raw_body(res, msgpack_metadata, sizeof(msgpack_metadata));
return true;
]])
for i = 1, #api do
local fn
local args = {}
fn = api[i]
output:write('\n case '..fn.id..':')
for j = 1, #fn.params do
local expected, convert, param
local idx = tostring(j - 1)
param = fn.params[j]
ref = '(req->via.array.ptr[3].via.array.ptr + '..idx..')'
-- decide which validation/conversion to use for this argument
if param[1] == 'array' then
expected = 'MSGPACK_OBJECT_ARRAY'
convert = 'msgpack_rpc_array_argument'
elseif param[1] == 'raw' then
expected = 'MSGPACK_OBJECT_RAW'
convert = 'msgpack_rpc_raw_argument'
elseif param[1] == 'integer' then
expected = 'MSGPACK_OBJECT_POSITIVE_INTEGER'
convert = 'msgpack_rpc_integer_argument'
end
output:write('\n if ('..ref..'->type != '..expected..') {')
output:write('\n return msgpack_rpc_error(req, res, "Wrong argument types");')
output:write('\n }')
table.insert(args, convert..'('..ref..')')
end
local call_args = table.concat(args, ', ')
-- convert the result back to msgpack
if fn.rtype == 'none' then
output:write('\n '..fn.fname..'('..call_args..');')
output:write('\n return msgpack_rpc_void_result(req, res);\n')
else
if fn.rtype == 'array' then
convert = 'msgpack_rpc_array_result'
elseif fn.rtype == 'raw' then
convert = 'msgpack_rpc_raw_result'
elseif fn.rtype == 'integer' then
convert = 'msgpack_rpc_integer_result'
end
output:write('\n return '..convert..'('..fn.fname..'('..call_args..'), req, res);\n')
end
end
output:write([[
default:
abort();
return false;
}
}
]])
output:close()
|