ic_agent 0.1.1
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/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Dockerfile +17 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +108 -0
- data/LICENSE.txt +21 -0
- data/README.md +217 -0
- data/Rakefile +4 -0
- data/ic_agent.gemspec +53 -0
- data/lib/ic_agent/agent.rb +176 -0
- data/lib/ic_agent/ast/assembler.rb +192 -0
- data/lib/ic_agent/ast/did_grammar.treetop +159 -0
- data/lib/ic_agent/ast/did_grammar_v1.treetop +111 -0
- data/lib/ic_agent/ast/nodes/named_nodes.rb +410 -0
- data/lib/ic_agent/ast/nodes/string_literal.rb +17 -0
- data/lib/ic_agent/ast/parser.rb +85 -0
- data/lib/ic_agent/ast/writer.rb +19 -0
- data/lib/ic_agent/candid.rb +1671 -0
- data/lib/ic_agent/canister.rb +270 -0
- data/lib/ic_agent/certificate.rb +55 -0
- data/lib/ic_agent/client.rb +47 -0
- data/lib/ic_agent/common/cycles_wallet.rb +275 -0
- data/lib/ic_agent/common/governance.rb +366 -0
- data/lib/ic_agent/common/ledger.rb +177 -0
- data/lib/ic_agent/common/management.rb +69 -0
- data/lib/ic_agent/identity.rb +125 -0
- data/lib/ic_agent/principal.rb +112 -0
- data/lib/ic_agent/system_state.rb +43 -0
- data/lib/ic_agent/utils.rb +71 -0
- data/lib/ic_agent/version.rb +5 -0
- data/lib/ic_agent.rb +37 -0
- data/sig/ic_agent.rbs +4 -0
- metadata +288 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
require 'cbor'
|
|
2
|
+
require 'ctf_party'
|
|
3
|
+
|
|
4
|
+
module IcAgent
|
|
5
|
+
class Request
|
|
6
|
+
def self.sign_request(req, iden)
|
|
7
|
+
req_id = IcAgent::Utils.to_request_id(req)
|
|
8
|
+
msg = IcAgent::IC_REQUEST_DOMAIN_SEPARATOR + req_id
|
|
9
|
+
sig = iden.sign(msg)
|
|
10
|
+
envelop = {
|
|
11
|
+
'content': req,
|
|
12
|
+
'sender_pubkey': sig[0],
|
|
13
|
+
'sender_sig': sig[1]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if iden.is_a?(DelegateIdentity)
|
|
17
|
+
envelop.update({
|
|
18
|
+
'sender_pubkey': iden.der_pubkey,
|
|
19
|
+
'sender_delegation': iden.delegations
|
|
20
|
+
})
|
|
21
|
+
end
|
|
22
|
+
[req_id, CBOR.encode(envelop)]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class Agent
|
|
27
|
+
attr_accessor :identity, :client, :ingress_expiry, :root_key, :nonce_factory
|
|
28
|
+
|
|
29
|
+
def initialize(identity, client, nonce_factory = nil, ingress_expiry = 300, root_key = IcAgent::IC_ROOT_KEY)
|
|
30
|
+
@identity = identity
|
|
31
|
+
@client = client
|
|
32
|
+
@ingress_expiry = ingress_expiry
|
|
33
|
+
@root_key = root_key
|
|
34
|
+
@nonce_factory = nonce_factory
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def get_principal
|
|
38
|
+
@identity.sender
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def get_expiry_date
|
|
42
|
+
((Time.now.to_i + @ingress_expiry) * 10**9).to_i
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def query_endpoint(canister_id, data)
|
|
46
|
+
ret = @client.query(canister_id, data)
|
|
47
|
+
decode_ret = nil
|
|
48
|
+
begin
|
|
49
|
+
decode_ret = CBOR.decode(ret)
|
|
50
|
+
rescue CBOR::MalformedFormatError
|
|
51
|
+
decode_ret = ret
|
|
52
|
+
# print logger
|
|
53
|
+
end
|
|
54
|
+
decode_ret
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def call_endpoint(canister_id, request_id, data)
|
|
58
|
+
@client.call(canister_id, request_id, data)
|
|
59
|
+
request_id
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def read_state_endpoint(canister_id, data)
|
|
63
|
+
result = @client.read_state(canister_id, data)
|
|
64
|
+
result
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def query_raw(canister_id, method_name, arg, return_type = nil, effective_canister_id = nil)
|
|
68
|
+
req_canister_id = canister_id.is_a?(String) ? Principal.from_str(canister_id).bytes : canister_id.bytes
|
|
69
|
+
req = {
|
|
70
|
+
'request_type' => 'query',
|
|
71
|
+
'sender' => @identity.sender.bytes,
|
|
72
|
+
'canister_id' => req_canister_id,
|
|
73
|
+
'method_name' => method_name,
|
|
74
|
+
'arg' => arg.hex2str,
|
|
75
|
+
'ingress_expiry' => get_expiry_date
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
_, data = Request.sign_request(req, @identity)
|
|
79
|
+
query_canister_id = effective_canister_id.nil? ? canister_id : effective_canister_id
|
|
80
|
+
result = query_endpoint(query_canister_id, data)
|
|
81
|
+
raise Exception, "Malformed result: #{result}" unless result.is_a?(CBOR::Tagged) && result.value.key?('status')
|
|
82
|
+
|
|
83
|
+
if result.value['status'] == 'replied'
|
|
84
|
+
arg = result.value['reply']['arg']
|
|
85
|
+
if arg[0..3] == 'DIDL'
|
|
86
|
+
IcAgent::Candid.decode(arg.to_hex, return_type)
|
|
87
|
+
else
|
|
88
|
+
arg
|
|
89
|
+
end
|
|
90
|
+
elsif result.value['status'] == 'rejected'
|
|
91
|
+
raise Exception, "Canister reject the call: #{result['reject_message']}"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def update_raw(canister_id, method_name, arg, return_type = nil, effective_canister_id = nil, **kwargs)
|
|
96
|
+
req_canister_id = canister_id.is_a?(String) ? Principal.from_str(canister_id).bytes : canister_id.bytes
|
|
97
|
+
req = {
|
|
98
|
+
'request_type' => 'call',
|
|
99
|
+
'sender' => @identity.sender.bytes,
|
|
100
|
+
'canister_id' => req_canister_id,
|
|
101
|
+
'method_name' => method_name,
|
|
102
|
+
'arg' => arg.hex2str,
|
|
103
|
+
'ingress_expiry' => get_expiry_date
|
|
104
|
+
}
|
|
105
|
+
req_id, data = Request.sign_request(req, @identity)
|
|
106
|
+
eid = effective_canister_id.nil? ? canister_id : effective_canister_id
|
|
107
|
+
_ = call_endpoint(eid, req_id, data)
|
|
108
|
+
status, result = poll(eid, req_id, **kwargs)
|
|
109
|
+
if status == 'rejected'
|
|
110
|
+
raise Exception, "Rejected: #{result.to_s}"
|
|
111
|
+
elsif status == 'replied'
|
|
112
|
+
if result[0..3] == 'DIDL'
|
|
113
|
+
IcAgent::Candid.decode(result.to_hex, return_type)
|
|
114
|
+
else
|
|
115
|
+
# Some canisters don't use DIDL (e.g. they might encode using json instead)
|
|
116
|
+
result
|
|
117
|
+
end
|
|
118
|
+
else
|
|
119
|
+
raise Exception, "Timeout to poll result, current status: #{status.to_s}"
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def read_state_raw(canister_id, paths)
|
|
124
|
+
req = {
|
|
125
|
+
'request_type' => 'read_state',
|
|
126
|
+
'sender' => @identity.sender.bytes,
|
|
127
|
+
'paths' => paths,
|
|
128
|
+
'ingress_expiry' => get_expiry_date
|
|
129
|
+
}
|
|
130
|
+
_, data = Request.sign_request(req, @identity)
|
|
131
|
+
ret = read_state_endpoint(canister_id, data)
|
|
132
|
+
if ret == 'Invalid path requested.'
|
|
133
|
+
raise ValueError, 'Invalid path requested!'
|
|
134
|
+
elsif ret == 'Could not parse body as read request: invalid type: byte array, expected a sequence'
|
|
135
|
+
raise ValueError, 'Could not parse body as read request: invalid type: byte array, expected a sequence'
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
begin
|
|
139
|
+
d = CBOR.decode(ret)
|
|
140
|
+
rescue StandardError
|
|
141
|
+
raise ValueError, "Unable to decode cbor value: #{ret}"
|
|
142
|
+
end
|
|
143
|
+
CBOR.decode(d.value['certificate'])
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def request_status_raw(canister_id, req_id)
|
|
147
|
+
paths = [['request_status', req_id]]
|
|
148
|
+
cert = read_state_raw(canister_id, paths)
|
|
149
|
+
status = IcAgent::Certificate.lookup(['request_status', req_id, 'status'], cert)
|
|
150
|
+
[status, cert]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def poll(canister_id, req_id, delay = 1, timeout = IcAgent::DEFAULT_POLL_TIMEOUT_SECS)
|
|
154
|
+
status = nil
|
|
155
|
+
cert = nil
|
|
156
|
+
(timeout / delay).to_i.times do
|
|
157
|
+
status, cert = request_status_raw(canister_id, req_id)
|
|
158
|
+
break if %w[replied done rejected].include?(status)
|
|
159
|
+
|
|
160
|
+
sleep(delay)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
if status == 'replied'
|
|
164
|
+
path = ['request_status', req_id, 'reply']
|
|
165
|
+
res = IcAgent::Certificate.lookup(path, cert)
|
|
166
|
+
[status, res]
|
|
167
|
+
elsif status == 'rejected'
|
|
168
|
+
path = ['request_status', req_id, 'reject_message']
|
|
169
|
+
msg = IcAgent::Certificate.lookup(path, cert)
|
|
170
|
+
[status, msg]
|
|
171
|
+
else
|
|
172
|
+
[status, _]
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
require 'ic_agent/candid'
|
|
2
|
+
|
|
3
|
+
module IcAgent
|
|
4
|
+
module Ast
|
|
5
|
+
class Assembler
|
|
6
|
+
TYPE_MAPPING = {}
|
|
7
|
+
|
|
8
|
+
def self.build_single_type(child_type)
|
|
9
|
+
IcAgent::Candid::BaseTypes.send(child_type)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.build_blob
|
|
13
|
+
IcAgent::Candid::BaseTypes.vec(IcAgent::Candid::BaseTypes.nat8)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.build_opt(child_type, key_types = {})
|
|
17
|
+
child_type = key_types[child_type].nil? ? build_type(child_type, key_types) : key_types[child_type]
|
|
18
|
+
IcAgent::Candid::BaseTypes.opt(child_type)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.build_vec(child_type, key_types = {})
|
|
22
|
+
child_type = key_types[child_type].nil? ? build_type(child_type, key_types) : key_types[child_type]
|
|
23
|
+
IcAgent::Candid::BaseTypes.vec(child_type)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.build_record(child_hash, multi_types = {}, key_types = {})
|
|
27
|
+
child_types = {}
|
|
28
|
+
child_hash.each_key do |key|
|
|
29
|
+
if multi_types[child_hash[key].strip]
|
|
30
|
+
multi_type = build_type(multi_types[child_hash[key].strip], key_types, multi_types)
|
|
31
|
+
child_types[key] = multi_type
|
|
32
|
+
elsif key_types[child_hash[key].strip]
|
|
33
|
+
child_types[key] = key_types[child_hash[key].strip]
|
|
34
|
+
else
|
|
35
|
+
child_types[key] = build_type(child_hash[key], key_types, multi_types)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
IcAgent::Candid::BaseTypes.record(child_types)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.build_variant(child_hash, multi_types = {}, key_types = {})
|
|
42
|
+
child_types = {}
|
|
43
|
+
child_hash.each_key do |key|
|
|
44
|
+
if multi_types[child_hash[key].strip]
|
|
45
|
+
multi_type = build_type(multi_types[child_hash[key].strip], multi_types)
|
|
46
|
+
child_types[key] = multi_type
|
|
47
|
+
elsif key_types[child_hash[key].strip]
|
|
48
|
+
child_types[key] = key_types[child_hash[key].strip]
|
|
49
|
+
else
|
|
50
|
+
child_types[key] = build_type(child_hash[key], key_types, multi_types)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
IcAgent::Candid::BaseTypes.variant(child_types)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.build_type(type_str, key_types = {}, multi_types = {})
|
|
57
|
+
opt_code = get_opt_code(type_str)
|
|
58
|
+
|
|
59
|
+
if IcAgent::Candid::SINGLE_TYPES.include? opt_code
|
|
60
|
+
build_single_type(opt_code)
|
|
61
|
+
elsif opt_code == 'blob'
|
|
62
|
+
build_blob
|
|
63
|
+
elsif opt_code == 'opt'
|
|
64
|
+
type_str = recover_type(type_str, multi_types)
|
|
65
|
+
child_code = get_child_code(type_str, ' ')
|
|
66
|
+
build_opt(child_code, key_types)
|
|
67
|
+
elsif opt_code == 'vec'
|
|
68
|
+
type_str = recover_type(type_str, multi_types)
|
|
69
|
+
child_code = get_child_code(type_str, ' ')
|
|
70
|
+
build_vec(child_code, key_types)
|
|
71
|
+
elsif opt_code == 'record'
|
|
72
|
+
child_code = get_record_content(type_str)
|
|
73
|
+
pure_child_code, multi_types = replace_multi_type(child_code)
|
|
74
|
+
child_hash = {}
|
|
75
|
+
key_index = 0
|
|
76
|
+
pure_child_code.split(';').each do |item|
|
|
77
|
+
item_key, item_value = get_record_key_value(item, ' : ', key_index)
|
|
78
|
+
child_hash[item_key] = item_value
|
|
79
|
+
key_index += 1
|
|
80
|
+
end
|
|
81
|
+
build_record(child_hash, multi_types, key_types)
|
|
82
|
+
elsif opt_code == 'variant'
|
|
83
|
+
child_code = get_variant_content(type_str)
|
|
84
|
+
pure_child_code, multi_types = replace_multi_type(child_code)
|
|
85
|
+
child_hash = {}
|
|
86
|
+
pure_child_code.split(';').each do |item|
|
|
87
|
+
item_arr = item.strip.split(' : ')
|
|
88
|
+
child_hash[item_arr[0]] = (item_arr.size > 1 ? item_arr[1] : 'null')
|
|
89
|
+
end
|
|
90
|
+
build_variant(child_hash, multi_types, key_types)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.replace_last_occurrence(string, pattern, replacement)
|
|
95
|
+
last_index = string.rindex(pattern)
|
|
96
|
+
return string unless last_index
|
|
97
|
+
|
|
98
|
+
string[last_index..-1] = replacement
|
|
99
|
+
string
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def self.get_record_content(record_str)
|
|
103
|
+
record_str = record_str.sub('record', '').sub('{', '')
|
|
104
|
+
record_str = replace_last_occurrence(record_str, '}', '')
|
|
105
|
+
record_str.strip
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def self.get_variant_content(variant_str)
|
|
109
|
+
variant_str = variant_str.sub('variant', '').sub('{', '')
|
|
110
|
+
variant_str = replace_last_occurrence(variant_str, '}', '')
|
|
111
|
+
variant_str.strip
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def self.get_record_key_value(item_str, index_str, key_index = 0)
|
|
115
|
+
first_index = item_str.index(index_str)
|
|
116
|
+
if first_index
|
|
117
|
+
key = item_str[0..first_index].strip
|
|
118
|
+
value = item_str[(first_index + index_str.size)..].strip
|
|
119
|
+
else
|
|
120
|
+
key = key_index.to_s
|
|
121
|
+
value = item_str.strip
|
|
122
|
+
end
|
|
123
|
+
return key, value
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def self.get_opt_code(item_str)
|
|
127
|
+
opt_code = item_str.strip
|
|
128
|
+
opt_code.split(' ')[0]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def self.get_child_code(item_str, index_str)
|
|
132
|
+
first_index = item_str.index(index_str)
|
|
133
|
+
item_str[(first_index + index_str.size)..].strip
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def self.replace_multi_type(type_str)
|
|
137
|
+
replaced_hash = {}
|
|
138
|
+
modified_str = type_str.gsub(/record\s*{[^{}]*}/) do |match|
|
|
139
|
+
rad_id = rand(100000..999999)
|
|
140
|
+
type_name = "record_#{rad_id}"
|
|
141
|
+
replaced_hash[type_name] = match
|
|
142
|
+
type_name
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
modified_str = modified_str.gsub(/variant\s*{[^{}]*}/) do |match|
|
|
146
|
+
rad_id = rand(100000..999999)
|
|
147
|
+
type_name = "variant_#{rad_id}"
|
|
148
|
+
replaced_hash[type_name] = match
|
|
149
|
+
type_name
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
return modified_str, replaced_hash
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def self.get_params_refer_values(type_str)
|
|
156
|
+
pure_child_code, replaced_hash = replace_multi_type(type_str)
|
|
157
|
+
key_index = 0
|
|
158
|
+
value_arr = []
|
|
159
|
+
pure_child_code.split(';').each do |item|
|
|
160
|
+
_, item_value = get_record_key_value(item, ' : ', key_index)
|
|
161
|
+
value_arr << item_value.split(' ')
|
|
162
|
+
key_index += 1
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
replaced_hash.each_key do |key|
|
|
166
|
+
item_arr = replaced_hash[key].strip.split(';')
|
|
167
|
+
item_arr.each do |item|
|
|
168
|
+
multi_item_arr = item.strip.split(':')
|
|
169
|
+
if multi_item_arr.size > 1
|
|
170
|
+
item_value_arr = multi_item_arr[1].strip.split(' ').collect { |v| v.strip.gsub(';', '') }
|
|
171
|
+
item_value_arr.delete('{')
|
|
172
|
+
item_value_arr.delete('}')
|
|
173
|
+
item_value_arr.delete('{}')
|
|
174
|
+
value_arr << item_value_arr
|
|
175
|
+
else
|
|
176
|
+
value_arr << multi_item_arr[1].strip
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
value_arr.delete(key)
|
|
180
|
+
end
|
|
181
|
+
value_arr.flatten.uniq - IcAgent::Candid::ALL_TYPES - replaced_hash.keys
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def self.recover_type(type_str, multi_types)
|
|
185
|
+
multi_types.each_key do |key|
|
|
186
|
+
type_str = type_str.gsub(key, multi_types[key])
|
|
187
|
+
end
|
|
188
|
+
type_str
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
grammar DIDGrammar
|
|
2
|
+
|
|
3
|
+
rule body
|
|
4
|
+
( type_declaration / service_declaration / comment / end_of_line )* <IcAgent::Ast::Nodes::DIDFile>
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
rule base_type
|
|
8
|
+
'bool' / 'text' / 'null' / 'reserved' / 'empty' / 'principal' / 'nat8' / 'nat16' / 'nat32' / 'nat64' / 'int8' / 'int16' / 'int32' / 'int64' / 'float32' / 'float64' /
|
|
9
|
+
'opt' / 'vec' / 'record' / 'variant' / 'service' / 'func' / 'nat' / 'int' / blob
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
rule base_type_single
|
|
13
|
+
'bool' / 'text' / 'null' / 'reserved' / 'empty' / 'principal' / 'nat8' / 'nat16' / 'nat32' / 'nat64' / 'int8' / 'int16' / 'int32' / 'int64' / 'float32' / 'float64' / 'nat' / 'int' / 'blob'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
rule base_type_base
|
|
17
|
+
base_type_single+ <IcAgent::Ast::Nodes::BaseTypeSingle>
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
rule base_type_code
|
|
21
|
+
[a-zA-Z0-9_]+ <IcAgent::Ast::Nodes::BaseTypeOther>
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
rule base_type_key
|
|
25
|
+
[a-zA-Z0-9_]+ <IcAgent::Ast::Nodes::BaseTypeKey>
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
rule base_type_vec
|
|
29
|
+
'vec' space ic_all_type <IcAgent::Ast::Nodes::BaseTypeVec>
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
rule base_type_opt
|
|
33
|
+
'opt' space ic_all_type <IcAgent::Ast::Nodes::BaseTypeOpt>
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
rule base_type_record
|
|
37
|
+
'record' space '{' space_and_line ic_base_content space_and_line '}' <IcAgent::Ast::Nodes::BaseTypeRecord>
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
rule base_type_record_null
|
|
41
|
+
'record {}' <IcAgent::Ast::Nodes::BaseTypeRecord>
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
rule base_type_variant
|
|
45
|
+
'variant' space '{' space_and_line ic_base_content space_and_line '}' <IcAgent::Ast::Nodes::BaseTypeVariant>
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
rule base_type_func
|
|
49
|
+
'func' space? "(" service_method_params? ")" space? "->" space? "(" service_method_return_type ")" space? method_query? <IcAgent::Ast::Nodes::BaseTypeFunc>
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
rule ic_all_type
|
|
53
|
+
base_type_base / base_type_vec / base_type_record / base_type_record_null / base_type_variant / base_type_opt / base_type_func / base_type_code
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
rule ic_base_content
|
|
57
|
+
(ic_base_type)+ <IcAgent::Ast::Nodes::BaseTypeContent>
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
rule ic_base_type
|
|
61
|
+
space? base_type_key space? (':' space ic_all_type)* optional_semicolon <IcAgent::Ast::Nodes::BaseTypeChild>
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
rule type_declaration
|
|
65
|
+
'type' space_and_line type_name space_and_line '=' space_and_line ic_all_type ';' end_of_line <IcAgent::Ast::Nodes::TypeDeclaration>
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
rule type_name
|
|
69
|
+
[a-zA-Z0-9_]+ <IcAgent::Ast::Nodes::TypeName>
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
rule comment
|
|
73
|
+
space? '//' space? rest_of_line <IcAgent::Ast::Nodes::Comment>
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
rule service_declaration
|
|
77
|
+
"service" space ":" space service_name* "{" end_of_line service_methods "}" <IcAgent::Ast::Nodes::Service>
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
rule service_name
|
|
81
|
+
"(" ic_service_name ")" space? "->" space?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
rule ic_service_name
|
|
85
|
+
[a-zA-Z0-9_]* <IcAgent::Ast::Nodes::IcServiceName>
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
rule service_methods
|
|
89
|
+
(service_item)+ <IcAgent::Ast::Nodes::IcServiceMethods>
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
rule service_item
|
|
93
|
+
space? service_method_name space? ":" space? "(" service_method_params? ")" space? "->" space? "(" service_method_return_type ")" space? method_query? ';' end_of_line? <IcAgent::Ast::Nodes::IcServiceItem>
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
rule service_method_params
|
|
97
|
+
param_element (", " param_element)* <IcAgent::Ast::Nodes::IcServiceMethodParams>
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
rule service_method_return_type
|
|
101
|
+
[a-zA-Z0-9_{}: ]* <IcAgent::Ast::Nodes::IcServiceMethodReturn>
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
rule method_query
|
|
105
|
+
[a-zA-Z0-9_]* <IcAgent::Ast::Nodes::IcServiceMethodQuery>
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
rule words
|
|
109
|
+
[a-zA-Z_] [a-zA-Z0-9_]*
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
rule param_element
|
|
113
|
+
[a-zA-Z0-9_ ]+
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
rule service_method_name
|
|
117
|
+
[a-zA-Z0-9_]* <IcAgent::Ast::Nodes::IcServiceMethodName>
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
rule string_without_spaces
|
|
121
|
+
[\S]* <IcAgent::Ast::Nodes::StringLiteral>
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
rule rest_of_line
|
|
125
|
+
[^\n]* <IcAgent::Ast::Nodes::StringLiteral>
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
rule end_of_line
|
|
129
|
+
[\n]+
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
rule block_record
|
|
133
|
+
'{}'
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
rule has_end_of_line
|
|
137
|
+
[\n]*
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
rule splite_code
|
|
141
|
+
' : '
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
rule space_and_line
|
|
145
|
+
[\s\n]*
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
rule space
|
|
149
|
+
[\s]+
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
rule start_space
|
|
153
|
+
[^\s]+
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
rule optional_semicolon
|
|
157
|
+
[;]*
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
grammar DIDGrammar
|
|
2
|
+
|
|
3
|
+
rule body
|
|
4
|
+
( type_declaration / service_declaration / comment / end_of_line )* <IcAgent::Ast::Nodes::DIDFile>
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
rule base_type
|
|
8
|
+
'bool' / 'text' / 'null' / 'reserved' / 'empty' / 'principal' / 'nat' / 'nat8' / 'nat16' / 'nat32' / 'nat64' / 'int' / 'int8' / 'int16' / 'int32' / 'int64' / 'float32' / 'float64' /
|
|
9
|
+
'opt' / 'vec' / 'record' / 'variant' / 'service' / 'func'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
rule type_declaration
|
|
13
|
+
"type" space type_name space "=" space type_input_base_type space "{" end_of_line? type_body "};" end_of_line <IcAgent::Ast::Nodes::TypeDeclaration>
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
rule type_name
|
|
17
|
+
[a-zA-Z0-9_]+ <IcAgent::Ast::Nodes::TypeName>
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
rule type_body
|
|
21
|
+
type_body_item* <IcAgent::Ast::Nodes::TypeBody>
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
rule type_input_base_type
|
|
25
|
+
base_type+ <IcAgent::Ast::Nodes::TypeInputBaseType>
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
rule type_body_item
|
|
29
|
+
type_body_item_obj (';' space)* <IcAgent::Ast::Nodes::TypeBodyItem>
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
rule type_body_item_obj
|
|
33
|
+
type_body_item_name ':'? type_body_item_value? <IcAgent::Ast::Nodes::TypeBodyItemObj>
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
rule type_body_item_name
|
|
37
|
+
[a-zA-Z0-9_ ]+ <IcAgent::Ast::Nodes::TypeBodyItemName>
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
rule type_body_item_value
|
|
41
|
+
[a-zA-Z0-9_ ]+ <IcAgent::Ast::Nodes::TypeBodyItemValue>
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
rule body_item_element
|
|
45
|
+
[a-zA-Z0-9_: ]+
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
rule comment
|
|
49
|
+
space? '//' space? rest_of_line <IcAgent::Ast::Nodes::Comment>
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
rule service_declaration
|
|
53
|
+
"service" space ":" space "(" ic_service_name ")" space? "->" space? "{" end_of_line service_methods "}" <IcAgent::Ast::Nodes::Service>
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
rule ic_service_name
|
|
57
|
+
[a-zA-Z0-9_]* <IcAgent::Ast::Nodes::IcServiceName>
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
rule service_methods
|
|
61
|
+
(service_item)+ <IcAgent::Ast::Nodes::IcServiceMethods>
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
rule service_item
|
|
65
|
+
space? service_method_name space? ":" space? "(" service_method_params? ")" space? "->" space? "(" service_method_return_type ")" space? method_query? ';' end_of_line? <IcAgent::Ast::Nodes::IcServiceItem>
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
rule service_method_params
|
|
69
|
+
param_element (", " param_element)* <IcAgent::Ast::Nodes::IcServiceMethodParams>
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
rule service_method_return_type
|
|
73
|
+
[a-zA-Z0-9_]* <IcAgent::Ast::Nodes::IcServiceMethodReturn>
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
rule method_query
|
|
77
|
+
[a-zA-Z0-9_]* <IcAgent::Ast::Nodes::IcServiceMethodQuery>
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
rule words
|
|
81
|
+
[a-zA-Z_] [a-zA-Z0-9_]*
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
rule param_element
|
|
85
|
+
[a-zA-Z0-9_ ]+
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
rule service_method_name
|
|
89
|
+
[a-zA-Z0-9_]* <IcAgent::Ast::Nodes::IcServiceMethodName>
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
rule string_without_spaces
|
|
93
|
+
[\S]* <IcAgent::Ast::Nodes::StringLiteral>
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
rule rest_of_line
|
|
97
|
+
[^\n]* <IcAgent::Ast::Nodes::StringLiteral>
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
rule end_of_line
|
|
101
|
+
[\n]+
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
rule space
|
|
105
|
+
[\s]+
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
rule start_space
|
|
109
|
+
[^\s]+
|
|
110
|
+
end
|
|
111
|
+
end
|