Как использовать библиотеку ply?
Предполагая, что у вас уже установлен Ply, вы должны начать с изучения учебных пособий на официальном веб-сайте Ply .Они хорошо написаны и им легко следовать.
Это хорошее начало, и если да, что мне из этого сделать?
Ply требуется определение токена, чтобы начатьс.Вы уже сделали это.Однако сложности возрастают, когда ваш лексер должен различать, скажем, строку типа «забыть» и зарезервированное ключевое слово, такое как for
.Библиотека обеспечивает хорошую поддержку переменных приоритетов для устранения неоднозначности грамматики.Это может быть так же просто, как определить приоритет как кортежи:
precedence = (
('left', 'STRING', 'KEYWORD'),
('left', 'MULTIPLY', 'DIVIDE')
)
Однако я рекомендую вам прочитать больше о лексерах и yacc , прежде чем углубляться в более продвинутые функции, такие как выражения и приоритет в Ply.Для начала вы должны создать простой числовой лексер, который успешно анализирует целые числа, операторы и символы в скобках.Я уменьшил определение токена, чтобы соответствовать этой цели.Следующий пример был изменен из официальных руководств.
Импорт библиотеки и определение токена :
import ply.lex as lex #library import
# List of token names. This is always required
tokens = [
# OPERATORS #
'PLUS' , # +
'MINUS' , # -
'MULTIPLY', # *
'DIVIDE', # /
'MODULO', # %
'NOT', # ~
'EQUALS', # =
# COMPARATORS #
'LT', # <
'GT', # >
'LTE', # <=
'GTE', # >=
'DOUBLEEQUAL', # ==
'NE', # !=
'AND', # &
'OR' , # |
# BRACKETS #
'LPAREN', # (
'RPAREN', # )
'LBRACE', # [
'RBRACE', # ]
'BLOCKSTART', # {
'BLOCKEND', # }
# DATA TYPES#
'INTEGER', # int
'FLOAT', # dbl
'COMMENT', # --
]
Определение правил регулярных выражений для простых токенов : Ply использует библиотеку Python re
для поиска совпадений регулярных выражений для токенизации.Каждый токен требует определения регулярного выражения.Сначала мы определим определения регулярных выражений для простых токенов.Каждое объявление правила начинается со специального префикса t_
, указывающего, что он определяет токен.
# Regular expression rules for simple tokens
t_PLUS = r'\+'
t_MINUS = r'-'
t_MULTIPLY = r'\*'
t_DIVIDE = r'/'
t_MODULO = r'%'
t_LPAREN = r'\('
t_RPAREN = r'\)'
t_LBRACE = r'\['
t_RBRACE = r'\]'
t_BLOCKSTART = r'\{'
t_BLOCKEND = r'\}'
t_NOT = r'\~'
t_EQUALS = r'\='
t_GT = r'\>'
t_LT = r'\<'
t_LTE = r'\<\='
t_GTE = r'\>\='
t_DOUBLEEQUAL = r'\=\='
t_NE = r'\!\='
t_AND = r'\&'
t_OR = r'\|'
t_COMMENT = r'\#.*'
t_ignore = ' \t' ignore spaces and tabs
Определение правил регулярных выражений для более сложных токенов подобных данныхтакие типы, как int, float и символы новой строки для отслеживания номеров строк.Вы заметите, что эти определения очень похожи на приведенные выше.
#Rules for INTEGER and FLOAT tokens
def t_INTEGER(t):
r'\d+'
t.value = int(t.value)
return t
def t_FLOAT(t):
r'(\d*\.\d+)|(\d+\.\d*)'
t.value = float(t.value)
return t
# Define a rule so we can track line numbers
def t_newline(t):
r'\n+'
t.lexer.lineno += len(t.value)
Добавьте некоторую обработку ошибок для недопустимых символов :
# Error handling rule
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
Сборка лексера :
lexer = lex.lex()
Проверка лексера с некоторыми входными данными, токенизация и печать токенов :
data = '''
[25/(3*40) + {300-20} -16.5]
{(300-250)<(400-500)}
20 & 30 | 50
# This is a comment
'''
# Give the lexer some input
lexer.input(data)
# Tokenize
for tok in lexer:
print(tok)
Вы можете добавить этот пример кода в файл скрипта Python, например new_lexer.py
, и запустить его как python new_lexer.py
.Вы должны получить следующий вывод.Обратите внимание, что входные данные состояли из символов новой строки ('\n'
), которые были успешно проигнорированы в выводе.
#Output
LexToken(LBRACE,'[',2,1)
LexToken(INTEGER,25,2,2)
LexToken(DIVIDE,'/',2,4)
LexToken(LPAREN,'(',2,5)
LexToken(INTEGER,3,2,6)
LexToken(MULTIPLY,'*',2,7)
LexToken(INTEGER,40,2,8)
LexToken(RPAREN,')',2,10)
LexToken(PLUS,'+',2,12)
LexToken(BLOCKSTART,'{',2,14)
LexToken(INTEGER,300,2,15)
LexToken(MINUS,'-',2,18)
LexToken(INTEGER,20,2,19)
LexToken(BLOCKEND,'}',2,21)
LexToken(MINUS,'-',2,23)
LexToken(INTEGER,16,2,24)
LexToken(FLOAT,0.5,2,26)
LexToken(RBRACE,']',2,28)
LexToken(BLOCKSTART,'{',3,30)
LexToken(LPAREN,'(',3,31)
LexToken(INTEGER,300,3,32)
LexToken(MINUS,'-',3,35)
LexToken(INTEGER,250,3,36)
LexToken(RPAREN,')',3,39)
LexToken(LT,'<',3,40)
LexToken(LPAREN,'(',3,41)
LexToken(INTEGER,400,3,42)
LexToken(MINUS,'-',3,45)
LexToken(INTEGER,500,3,46)
LexToken(RPAREN,')',3,49)
LexToken(BLOCKEND,'}',3,50)
LexToken(INTEGER,20,4,52)
LexToken(AND,'&',4,55)
LexToken(INTEGER,30,4,57)
LexToken(OR,'|',4,60)
LexToken(INTEGER,50,4,62)
LexToken(COMMENT,'# This is a comment',5,65)
Существует множество других функций, которые вы можете использовать.Например, отладку можно включить с помощью lex.lex(debug=True)
.Официальные руководства содержат более подробную информацию об этих функциях.
Надеюсь, это поможет вам начать работу.Вы можете расширить код, добавив зарезервированные ключевые слова, такие как if
, while
и идентификацию строки с помощью STRING
, идентификацию символа с помощью CHAR
.Учебные руководства охватывают реализацию зарезервированных слов, определяя отображение словаря значения ключа следующим образом:
reserved = {
'if' : 'IF',
'then' : 'THEN',
'else' : 'ELSE',
'while' : 'WHILE',
...
}
, расширяя список токенов далее, определяя зарезервированный токен type
как 'ID'
и включая reserved
DICT значения: tokens.append('ID')
и tokens = tokens + list(reserved.values())
.Затем добавьте определение для t_ID
, как указано выше.
Есть ли какие-либо ресурсы, которые я могу использовать, чтобы помочь мне в этом.
Есть много ресурсов, доступных для изучения лексеров, парсеров и компиляторов.Вы должны начать с хорошей книги, которая охватывает теорию и реализацию.Есть много книг, которые охватывают эти темы.Мне понравилось это один . Вот другой ресурс, который может помочь.Если вы хотите изучить похожие библиотеки или ресурсы Python, этот SO ответ может помочь.