2015-02-01 2 views
1

AFAIK техники для исходного лексического Python кода:PLY - возвращает несколько маркеров

  • Когда уровень отступа текущей строки меньше, чем предыдущая строки х, производит DEDENT. Производите несколько DEDENT, если он закрывает несколько INDENT.
  • Когда конец ввода достигнут, произведите НАЧАЛЬНИК (ы), если есть незакрытый INDENT (s).

Теперь, используя PLY:

  • Как вернуть несколько жетонов из t_definition?
  • Как создать t_definition, вызванное при достижении EOF? Простой \Z не работает - PLY жалуется, что он соответствует пустой строке.

ответ

2

Насколько я знаю, PLY не реализует интерфейс синтаксического анализа, так как это наиболее легко решить эту проблему с бизоном. Тем не менее, очень легко ввести свою собственную lexer-оболочку, которая может обрабатывать очередь токенов-разделителей.

Минимальная реализация лексера должна реализовать метод , который возвращает объект с атрибутами type и value. (Вам также понадобится, если ваш парсер использует его, но я не буду беспокоиться об этом здесь.)

Теперь предположим, что базовый (PLY-сгенерированный) лексер производит токены NEWLINE, значение которых является длиной ведущей пробел после новой строки. Если некоторые строки не участвуют в алгоритме INDENT/DEDENT, NEWLINE должен быть подавлен для этих строк; мы не рассматриваем этот случай здесь. Упрощенная функция примера лексического (который работает только с пробелами, а не вкладки) может быть:

# This function doesn't handle tabs. Beware! 
def t_NEWLINE(self, t): 
    r'\n(?:\s*(?:[#].*)?\n)*\s*' 
    t.value = len(t.value) - 1 - t.value.rfind('\n') 
    return t 

Теперь оберните PLY сгенерированных лексер с оболочкой, которая имеет дело с абзацами:

# WARNING: 
# This code hasn't been tested much and it also may be inefficient 
# and/or inexact. It doesn't do python-style tab handling. Etc. etc. 

from collections import namedtuple, deque 

# These are the tokens. We only generate one of each here. If 
# we used lineno or didn't trust the parser to not mess with the 
# token, we could generate a new one each time. 
IndentToken = namedtuple('Token', 'type value') 
dedent = IndentToken('DEDENT', None) 
indent = IndentToken('INDENT', None) 
newline= IndentToken('NEWLINE', None) 

class IndentWrapper(object): 

    def __init__(self, lexer): 
    """Create a new wrapper given the lexer which is being wrapped""" 
    self.lexer = lexer 
    self.indent_stack = [0] 
    # A queue is overkill for this case, but it's simple. 
    self.token_queue = deque() 
    # This is just in case the ply-generated lexer cannot be called again 
    # after it returns None. 
    self.eof_reached = False 

    def token(self): 
    """Return the next token, or None if end of input has been reached""" 
    # Do we have any queued tokens? 
    if self.token_queue: 
     return self.token_queue.popleft() 
    # Are we done? 
    if self.eof_reached: 
     return None 
    # Get a token 
    t = self.lexer.token() 
    if t is None: 
     # At end of input, we might need to send some dedents 
     self.eof_reached = True 
     if len(self.indent_stack) > 1: 
     t = dedent 
     for i in range(len(self.indent_stack) - 1): 
      self.token_queue.append(dedent) 
     self.indent_stack = [0] 
    elif t.type == "NEWLINE": 
     # The NEWLINE token includes the amount of leading whitespace. 
     # Fabricate indent or dedents as/if necessary and queue them. 
     if t.value > self.indent_stack[-1]: 
     self.indent_stack.append(t.value) 
     self.token_queue.append(indent) 
     else: 
     while t.value < self.indent_stack[-1]: 
      self.indent_stack.pop() 
      self.token_queue.append(dedent) 
     if t.value != self.indent_stack[-1]: 
      raise IndentError # Or however you indicate errors 
    return t