Сканування

Сканер

Ми «читатимемо» запропонований конфігураційний файл таким чином:

  • Конфігураційний файл містить класи («classes»)
  • Кожен клас починається «елементом» типу «class»
  • … та містить ідентифікатор класу (типу «classid») та «блок» («block»)
  • Блок містить інші параметри класу.

Використовуючи SPARK ми створимо клас, який наслідує we will spark.GenericScanner клас та визначимо регулярні вирази для розпізнавання елементів у рядках документації Python (docstrings) відповідних методів. Таким чином для кожного «елемента», що відповідає регулярному виразу „[a-zA-Z_]+[0-9]*‘, буде викликано t_string і т.д, і т.п:

#!/usr/bin/env python
 
import spark
import token
 
class SimpleScanner(spark.GenericScanner):
def __init__(self):
    spark.GenericScanner.__init__(self)
    self.keywords = [
        '<strong>class</strong>',
        'rate',
        'ceil',
        'descr',
        'root',
        'parent',
    ]
 
def tokenize(self, input):
    self.rv = []
    spark.GenericScanner.tokenize(self, input)
    return self.rv
 
def t_whitespace(self, s):
    r'\s+'
    pass
 
def t_comment(self, s):
    r'\<em>#.*'</em>
    pass
 
def t_semicol(self, s):
    r';'
    self.rv.append(token.Token(type=s, attr=s))
 
def t_openblock(self, s):
    r'{'
    self.rv.append(token.Token(type=s, attr=s))
 
def t_closeblock(self, s):
    r'}'
    self.rv.append(token.Token(type=s, attr=s))
 
def t_equal(self, s):
    r'='
    self.rv.append(token.Token(type=s, attr=s))
 
def t_number(self, s):
    r'[0-9]+'
    self.rv.append(token.Token(type='number', attr=s))
 
def t_classid(self,s):
    r'[0-9]+:[0-9]+'
    self.rv.append(token.Token(type='classid', attr=s))
 
def t_keyword(self, s):
    # r' class | irate | iceil | descr '
    self.rv.append(token.Token(type=s, attr=s))
 
def t_string(self, s):
    r'[a-zA-Z_]+[0-9]*'
    if s in self.keywords:
        self.t_keyword(s)
    else:
        self.rv.append(token.Token(type='string', attr=s))

Що ж нам дає для розв’язання нашої задачі такий відносно простий клас? Спробуємо таке:

#!/usr/bin/env python
 
import spark
import scanner
 
def scan(f):
    input = f.read()
    scnr = scanner.SimpleScanner()
    return scnr.tokenize(input)
 
f = open('test.confg')
 
scanned = scan(f)
 
print scanned

`print scanned‘ надрукує послідовність елементів (я розбив рядок на коротші):

[class, 1:5, root, {, rate, =, 10240, ceil, =, 20480, },
 class, 1:50, parent, 1:5, {, ceil, =, 2048, rate, =, 1024, descr, =, My_favorite_client, },
 class, 1:53, parent, 1:50, {, descr, =, My_other_client, ceil, =, 2048, rate, =, 1024, }
]

Не так і погано: наш конфігураційний файл розібрано на елементи!

Чудово, далі:

>>> print '\n'.join(['Token "%s" of type "%s"' % (x.attr, x.type) for x in scanned])
Token "class" of type "class"
Token "1:5" of type "classid"
Token "root" of type "root"
Token "{" of type "{"
Token "rate" of type "rate"
Token "=" of type "="
Token "10240" of type "number"
Token "ceil" of type "ceil"
 
<... etc-etc ...>
 
Token "1024" of type "number"
Token "}" of type "}"

Отже, ми «зісканували» нашу конфігурацію успішно — ми маємо список елементів (list of tokens) з іхніми типами (клас token.Token визначає типи елементів — я його не подав, але він практично не відрізняється від поданого автором SPARK, John Aycock, у його документації).

Робимо лексичні помилки

Напишемо «roo*» замість «root» у 3-му рядку і спробуємо ще раз. Матимемо:

Specification error: unmatched input

Бачимо, що SPARK не пише номери рядків, де було «зіскановано» помилку. Читайте підручник SPARK, там йдестья про те, як це «виправити».

Але що станеться, якщо напишемо таке?

class 1:50 parent 1:5 {
    ceil = foo moo bar
    rate = 1024
}

Ми маємо «успішно» зіскановний список елементів:

. . .
Token "ceil" of type "ceil"
Token "=" of type "="
Token "foo" of type "string"
Token "moo" of type "string"
Token "bar" of type "string"
. . .

Отже, з лексичної точки зору — все нормально. Але, безумовно, це помилка :-)

Але це задача для наступного кроку — Парсингу.

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *

Цей сайт використовує Akismet для зменшення спаму. Дізнайтеся, як обробляються ваші дані коментарів.