Пропуск определенного токена числа токенов в Python PLY - PullRequest
0 голосов
/ 31 августа 2018

Итак, у меня есть язык, представляющий собой строку байтов, представляющую список следующих комбинаций заголовок + данные (например, 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]]

На практике это создает список байтов, но он просто смешивает ВСЕ оставшиеся данные после заголовка первого сегмента.

1 Ответ

0 голосов
/ 31 августа 2018

Если в вашем языке нет чего-то большего, то контекстно-свободный синтаксический анализатор действительно не подходит. Вы, вероятно, могли бы заставить квадратный колышек в круглое отверстие, но зачем?

Заголовки фиксированной длины можно действительно легко разделить на части, используя struct.unpack_from, и, получив длину полезной нагрузки, вы можете извлечь ее с помощью обычного фрагмента Python. Предположительно, вы бы затем проверили контрольную сумму, прежде чем пытаться что-либо сделать с помощью строки байтов.

Если полезная нагрузка содержит контент на языке, описанном безконтекстной грамматикой, вы можете использовать синтаксический анализатор на основе Ply для анализа только строки.

...