Add lexer/parser

This commit is contained in:
lash 2024-10-27 02:55:06 +00:00
parent 7937eb5187
commit 7a475d1a9f
Signed by: lash
GPG Key ID: 21D2E7BB88C2A746
3 changed files with 272 additions and 10 deletions

View File

@ -20,29 +20,32 @@ Ids are strings. Uuid strings are preferred.
The command processor **MUST** ensure that all ids are unique.
If `-` is supplied as `id`, a unique, random (uuid4) id will be generated.
If `-` is supplied as `id`, a unique, random (uuid4) id **MUST** be generated. All commands return the (given or generated) id.
All state change commands must implement a success or fail state, that may be used for the control commands (see below).
Actions depending on other created assets (keys, vouchers) that have failed to execution **MUST** abort further execution and raise exception.
Further execution **MUST** aborted and exception **MUST** be raised if:
* Actions depending on other created assets (keys, vouchers) that have failed to execute
* A store cannot handle a command (e.g. the custodial store may not handle voucher creation)
### Create a new key
`KEY_CREATE <id | "-"> <string:key_name> <string:store_name> -> hex:wallet_address`
`KEY_CREATE <id | "-"> <string:key_name> <string:store_name> [hex:private_key] -> id`
The initial use-case for the `store` is to choose between custodial system and independent keystore (simulating sovereign key holders). Future use-cases may need to use other stores aswell.
The initial use-case for the `store` is to choose between custodial system and independent keystore (simulating sovereign key holders). Future use-cases may need to use other stores aswell (e.g. a HTTP service that stores keystore files for given private keys).
The `key_name` or the returned `address` may be used to refer to the key after the command is executed.
Further execution **MUST** aborted and exception **MUST** be raised if private key is given and the store cannot store provided private keys.
### Create a new voucher
`VOUCHER_CREATE <id | "-"> <string:voucher_symbol> <string:voucher_name> <int:decimals> [<int:decay_factor> <int:decay_period> [hex:sink_address]] -> hex:contract_address`
Voucher create **MUST** guarantee unique token symbol. The processor **SHOULD**
-> e.g. if collision, append "_<2 bytes hex rand>"
`VOUCHER_CREATE <id | "-"> <string:voucher_symbol> <string:voucher_name> <int:decimals> [<int:decay_factor> <int:decay_period> [hex:sink_address]] -> id`
Voucher create **MUST** guarantee unique token symbol. If the requested symbol is already in use, the processor **SHOULD** try to append random ascii data to the symbol and try again until success.
The "decay" parameters specify that a contract with demurrage features must be used. If `sink_address` is not set, it defaults to the burn address (`0x000000000000000000000000000000000000dEaD`).
@ -53,14 +56,14 @@ The command processor **SHOULD** store the executing key as the "owner" of the v
### Mint vouchers
`VOUCHER_MINT <id | "-"> <string:voucher_symbol | hex:voucher_address> <int:amount> [<to_account | to_key_name>] -> hex:to_account`
`VOUCHER_MINT <id | "-"> <string:voucher_symbol | hex:voucher_address> <int:amount> [<to_account | to_key_name>] -> id`
If `to_*` is not specified, the recipient should be the owner of the voucher.
### Transfer vouchers
`VOUCHER_TRANSFER <id | "-"> <voucher_symbol | voucher_address> <amount> <to_account | to_key_name> <from_account | from_key_name > -> hex:from_account`
`VOUCHER_TRANSFER <id | "-"> <voucher_symbol | voucher_address> <amount> <to_account | to_key_name> <from_account | from_key_name > id`
## Control commands
@ -82,3 +85,12 @@ Continue after execution, regardless of result.
`NEED <id | ->`
Continue after execution, but raise exception and abort execution if any of the commands fail.
## Report commands
In the event that an interactive tool would be implemented for the protocol, some assistance for puny humans would be required aswell.
`PEEK <id>`
Return human-readable version of the issued command, its current state, and its associated data (e.g. wallet address, contract address).

249
parse.py Normal file
View File

@ -0,0 +1,249 @@
import ply.lex as lex
import ply.yacc as yacc
import enum
import uuid
tokens = (
'ID',
'NOID',
'HEX',
'LABEL',
'VALUE',
'END',
)
t_ID = r'[a-zA-Z0-9]+(-[a-zA-Z0-9]+)+'
t_NOID = r'\-'
t_LABEL = r'[a-zA-Z_]+'
t_VALUE = r'\d+'
t_END = r'\n'
t_HEX = r'0x([a-fA-F0-9])+'
#def t_VALUE(t):
# r'\d+'
# t.value = int(t.value)
#def t_HEX(t):
# r'0x([a-fA-F0-9])+'
#print(t)
#try:
# t.value = bytes.fromhex(t.value)
#except:
# return False
#return t.value
#t.value = t.value
# pass
t_ignore = ' \t'
def t_error(t):
print("problem: ", t.value[0])
t.lexer.skip(1)
lexer = lex.lex()
#
#data = '''
#FOOBAR uf-2etg 0xa3bdefa momo 123
#
#BARBAR
#BAZ
#
#BASFB foo-bar-baz
#'''
#
#lexer.input(data)
#
#while True:
# tok = lexer.token()
# if not tok:
# break
# print(tok)
class CmdId(enum.IntEnum):
KEY_CREATE = 0x1
VOUCHER_CREATE = 0x10
VOUCHER_MINT = 0x11
VOUCHER_TRANSFER = 0x12
WAIT = 0x20
NEED = 0x21
PEEK = 0x80
class Agent:
def __str__(self):
return self.__class__.__name__ + ':' #{}'.format(self.__class__.__name__, self.v)
class AddressAgent(Agent):
def __init__(self, v):
self.v = bytes.fromhex(v[2:])
def __str__(self):
return Agent.__str__(self) + self.v.hex()
class NameAgent(Agent):
def __init__(self, v):
self.v = v
def __str__(self):
return Agent.__str__(self) + self.v
class Cmd:
def __init__(self, cmd):
self.c = CmdId[cmd]
self.i = None
self.a = None
self.f = None
self.t = None
self.v = 0
self.k = None
self.d = 0
self.p = 0
self.s = None
self.n = None
def __str__(self):
return "[{:02d}]{} i={} v={} a={} f={} t={} k={} d={} p={} s={} n={}".format(self.c, self.c.name, self.i, self.v, self.a, self.f, self.t, self.k, self.d, self.p, self.s, self.n)
def to_store(v):
return str(v)
def to_agent(v):
r = None
try:
r = AddressAgent(v)
except ValueError:
r = NameAgent(v)
return r
def p_cmd(p):
'''cmd : key_create
| voucher_mint
| voucher_transfer
| voucher_create
| pair
'''
p[0] = p[1]
def p_key_create(p):
'''key_create : change_label LABEL LABEL
| change_label LABEL LABEL HEX
'''
o = p[1]
o.f = NameAgent(p[2])
o.t = to_store(p[3])
if len(p) > 4:
o.k = p[4]
p[0] = o
def p_voucher_mint(p):
'''voucher_mint : change_label hv
| change_label nv
'''
o = p[1]
if o.c.value & 0x10 == 0:
raise ValueError("not a voucher command")
o.v = int(p[2][1])
o.a = to_agent(p[2][0])
p[0] = o
def p_voucher_create(p):
'''voucher_create : change_label LABEL LABEL VALUE
| change_label LABEL LABEL VALUE VALUE VALUE
'''
o = p[1]
if o.c.value & 0x10 == 0:
raise ValueError("not a voucher command")
o.s = p[2]
o.n = p[3]
o.v = int(p[4])
if len(p) > 5:
o.d = p[5]
o.p = p[6]
p[0] = o
def p_voucher_mint_recipient(p):
'''voucher_mint : change_label hv HEX
| change_label nv HEX
| change_label hv LABEL
| change_label nv LABEL
'''
o = p[1]
if o.c.value & 0x10 == 0:
raise ValueError("not a voucher command")
o.v = int(p[2][1])
o.a = to_agent(p[2][0])
o.t = to_agent(p[3])
p[0] = o
def p_voucher_transfer(p):
'''voucher_transfer : change_label hv HEX HEX
| change_label nv HEX HEX
| change_label hv LABEL HEX
| change_label nv LABEL HEX
'''
o = p[1]
if o.c.value & 0x10 == 0:
raise ValueError("not a voucher command")
o.v = int(p[2][1])
o.a = to_agent(p[2][0])
o.t = to_agent(p[3])
o.f = to_agent(p[4])
p[0] = o
def p_nv(p):
'nv : LABEL VALUE'
p[0] = (p[1], p[2],)
def p_hv(p):
'hv : HEX VALUE'
p[0] = (p[1], p[2],)
def p_change_label(p):
'''change_label : pair
| pairnoid
'''
p[0] = p[1]
def p_pair(p):
'pair : LABEL ID'
o = Cmd(p[1])
o.i = p[2]
p[0] = o
def p_pairnoid(p):
'pairnoid : LABEL NOID'
o = Cmd(p[1])
o.i = str(uuid.uuid4())
p[0] = o
parser = yacc.yacc()
print("result: {}".format(parser.parse("WAIT bar-baz")))
print("result: {}".format(parser.parse("KEY_CREATE bar-baz Foo BAr")))
print("result: {}".format(parser.parse("KEY_CREATE bar-baz Foo BAr 0xabcdef0")))
print("result: {}".format(parser.parse("VOUCHER_MINT - 0xdeadbeef 13 0xfdaf")))
print("result: {}".format(parser.parse("VOUCHER_MINT bar-baz 0xdeadbeef 13 0xfdaf")))
print("result: {}".format(parser.parse("VOUCHER_MINT bar-baz 0xdeadbeef 42")))
print("result: {}".format(parser.parse("VOUCHER_CREATE bar-baz Foo Bar 42")))
print("result: {}".format(parser.parse("VOUCHER_CREATE bar-baz Foo Bar 42 44 233")))
print("result: {}".format(parser.parse("VOUCHER_TRANSFER bar-baz 0xbeeffeed0123 666 foo 0x0fdc")))

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
ply==3.11