Таким образом, у меня есть язык, на котором bytestring представляет список следующих headerdataheaderdataheaderdata...
заголовка + данных (например, headerdataheaderdataheaderdata...
):
datalen
datalen
минус 26 байтов, которые могут содержать разделительВсего один токен за байтов:
b00 = r'\x00'
...
bFF = r'\xFF'
file -> segments
segments -> segment segment
| segment
segment -> delim id timestamp type group_id owner datalen checksum
delim -> bFF bAA
id -> int32
timestamp -> int32
type -> int32
group_id -> int16
owner -> int32
datalen -> int32
checksum -> int32
int32 -> byte byte byte byte
int16 -> byte byte
byte -> <oh my god one rule per value token>
Я знаю, что это не типичный контекстный бесплатный язык, с которым вы обычно работаете в PLY. Длина каждого сегмента зависит от числа, содержащегося в нем. Однако легко получить эти данные как встроенное действие в правиле "сегмента":
def p_segment(t):
''' segment : delim id timestamp type group_id owner datalen checksum'''
id = t[2]
timestamp = t[3]
type = t[4]
group_id = t[5]
owner = t[6]
datalen = t[7]
checksum = t[8]
t[0] = (id,timestamp,type,group_id,owner,datalen,checksum)
# Assume all rules for t[2:8] return the correct data type haha
Теперь я думал о том, чтобы просто аккумулировать дополнительные байты и хранить их где-нибудь с помощью lexer.token()
:
def p_segment(t):
''' segment : delim id timestamp type group_id owner datalen checksum'''
id = t[2]
timestamp = t[3]
type = t[4]
group_id = t[5]
owner = t[6]
datalen = t[7]
checksum = t[8]
data = []
for i in range(datalen):
data += t.lexer.token()
t[0] = (id,timestamp,type,group_id,owner,datalen,checksum,data)
Это работает на extent-- data
действительно есть данные в нем, и t.lexer.lexpos
обновляется, однако анализатор теряет свои шарики с синтаксической ошибкой сразу после последнего байта заголовка. Это, по-видимому, означает, что, хотя лексер продвигается вдоль строки, синтаксический анализатор не является. Как я могу это исправить? Должен ли я вообще отказаться от PLY? (И если да, то какая подходящая альтернатива?)
Кроме того, я попытался добавить правило для данных, но просто добавление правила 'segment_data' действительно не работает, поскольку нет ограничителя или какой-либо контекстно-свободной длины, на которую можно положиться:
def p_segment_data(t):
'''
segment_data : byte segment-data
| byte
'''
if len(t) > 2:
t[0] = [t[1]] + t[2] # we want to return a list of bytes
else:
t[0] = [t[1]]
Это на практике генерирует список байтов, но он просто обходит ВСЕ оставшиеся данные после заголовка первого сегмента.
Если на вашем языке нет чего-то большего, то контекстно-свободный парсер действительно не является подходящим инструментом. Вероятно, вы могли бы заставить квадратный колышек в круглое отверстие, но зачем беспокоиться?
Заголовки с фиксированной длиной можно легко struct.unpack_from
, используя struct.unpack_from
, и как только у вас будет длина полезной нагрузки, вы можете извлечь ее с помощью обычного кусочка Python. Предположительно, вы проверили бы контрольную сумму, прежде чем пытаться что-либо сделать с помощью байта.
Если полезная нагрузка содержит контент на языке, описанном без контекстной грамматики, вы можете использовать парсер, основанный на Ply, для синтаксического анализа только строки.