Итак, у меня есть язык, представляющий собой строку байтов, представляющую список следующих комбинаций заголовок + данные (например, headerdataheaderdataheaderdata...
):
Заголовок
- 18 байт, которые не имеют значения (разделитель, идентификатор, метка времени, тип и т. Д.)
- 4 байта, которые определяют длину данных и объединенного заголовка. Давайте назовем это
datalen
- еще 4 байта (контрольная сумма)
Данные
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)
Это работает до некоторой степени - в data
есть данные, и t.lexer.lexpos
обновляется, однако анализатор теряет свои шарики с синтаксической ошибкой сразу после последнего байта заголовка.
Кажется, это подразумевает, что в то время как лексер продвигается вперед по строке, парсер - нет. Как я могу это исправить? Должен ли я вообще отказаться от PLY? (И если да, то какова подходящая альтернатива?)
Также я попытался добавить правило для данных, но простое добавление правила 'plot_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]]
На практике это создает список байтов, но он просто смешивает ВСЕ оставшиеся данные после заголовка первого сегмента.