1
0
mirror of https://github.com/lightning/bolts.git synced 2024-11-19 01:50:03 +01:00
lightning-bolts/tools/extract-formats.py
Rusty Russell ed107e4ef0 tools/extract-formats.py: allow fields with _ in them.
eg. BOLT 4.

See also: #141

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2017-03-31 14:14:29 +10:30

118 lines
3.8 KiB
Python
Executable File

#! /usr/bin/python3
# Simple script to parse specs and produce CSV files.
# Released by Rusty Russell under CC0: https://creativecommons.org/publicdomain/zero/1.0/
from optparse import OptionParser
import sys
import re
import fileinput
# Figure out if we can determine type from size.
def guess_alignment(message,name,sizestr):
# Exceptions:
# - Padding has no alignment requirements.
# - channel-id is size 8, but has alignment 4.
# - node_announcement.ipv6 has size 16, but alignment 4 (to align IPv4 addr).
# - node_announcement.alias is a string, so alignment 1
# - signatures have no alignment requirement.
if name.startswith('pad'):
return 1
if name == 'channel-id':
return 4
if message == 'node_announcement' and name == 'ipv6':
return 4
if message == 'node_announcement' and name == 'alias':
return 1
if 'signature' in name:
return 1
# Size can be variable.
try:
size = int(sizestr)
except ValueError:
# If it contains a "*xxx" factor, that's our per-unit size.
s = re.search('\*([0-9]*)$', sizestr)
if s is None:
size = 1
else:
size = int(s.group(1))
if size % 8 == 0:
return 8
elif size % 4 == 0:
return 4
elif size % 2 == 0:
return 2
return 1
parser = OptionParser()
parser.add_option("--message-types",
action="store_true", dest="output_types", default=False,
help="Output MESSAGENAME,VALUE for every message")
parser.add_option("--check-alignment",
action="store_true", dest="check_alignment", default=False,
help="Check alignment for every member of each message")
parser.add_option("--message-fields",
action="store_true", dest="output_fields", default=False,
help="Output MESSAGENAME,OFFSET,FIELDNAME,SIZE for every message")
(options, args) = parser.parse_args()
# Example inputs:
# 1. type: 17 (`error`)
# 2. data:
# * [8:channel-id]
# * [4:len]
# * [len:data]
#
# 1. type: PERM|NODE|3 (`required_node_feature_missing`)
message = None
havedata = None
typeline = re.compile('1\. type: (?P<value>[-0-9A-Za-z_|]+) \(`(?P<name>[-A-Za-z_]+)`\)')
dataline = re.compile('\s+\* \[(?P<size>[-a-z0-9*+]+):(?P<name>[-a-z0-9_]+)\]')
for i,line in enumerate(fileinput.input(args)):
line = line.rstrip()
linenum = i+1
match = typeline.fullmatch(line)
if match:
if message is not None:
raise ValueError('{}:Found a message while I was already in a message'.format(linenum))
message = match.group('name')
if options.output_types:
print("{},{}".format(match.group('name'), match.group('value')))
havedata = None
elif message is not None and havedata is None:
if line != '2. data:':
message = None
havedata = True
dataoff = 0
off_extraterms = ""
maxalign = 1
elif message is not None and havedata is not None:
match = dataline.fullmatch(line)
if match:
align = guess_alignment(message, match.group('name'), match.group('size'))
if options.check_alignment and dataoff % align != 0:
raise ValueError('{}:message {} field {} Offset {} not aligned on {} boundary:'.format(linenum, message, match.group('name'), dataoff, align))
if options.output_fields:
print("{},{}{},{},{}".format(message,dataoff,off_extraterms,match.group('name'),match.group('size')))
# Size can be variable.
try:
dataoff += int(match.group('size'))
except ValueError:
# Offset has variable component.
off_extraterms = off_extraterms + "+" + match.group('size')
else:
message = None