На цьому кроці ми хочемо перевірити нашу конфігурацію на відсутність логічних помилок.
Що савме ми перевірятимемо? Я пропонуж такі тести:
- Кожен клас ПОВИНЕН мати визначення «
rate
«. - Кожен клас ПОВИНЕН мати визначення «
parent
«, воно ПОВИННО вказувати на визначений клас.
Ми створимо підклас класу spark.GenericASTTraversal
, який передивлятиметься все дерево и викликатиме методи залежно від назви (типу, фактично) вузла — викличе n_classdef
для вузла «classdef
» тощо.
Код:
#!/usr/bin/env python import spark from helper import getValue defined_classes = [] requested_parents = [] class HTBCheckTree(spark.GenericASTTraversal): def __init__(self, ast): spark.GenericASTTraversal.__init__(self, ast) self.postorder() def n_root(self, node): defined_classes.append(node.classid) def n_classdef(self, node): if not getValue(node, 'rate'): print "Error: `rate' for class MUST be set.\n" raise RuntimeError defined_classes.append(node.classid) requested_parents.append(node.parent) def n_classes(self, node): for p in requested_parents: if p.attr not in [ d.attr for d in defined_classes ]: print "Error: class %s does not exist.\n" % p.attr raise RuntimeError |
Все просто :-)
У нашому випадку — ми повинні мати методи для опрацювання лише вузлів певних типів. Кожен вузол «classdef
» перевіряє, чи визначено «rate
«, та додає параметри «classid
» та «parent
» до відповідних списків. «Кореневий» вузол також додає «classid
» до списку. І оскільки SPARK переглядає дерево знизу вгору (від «листків» до «кореня» — від «classdef
» до «classes
» у нашому випадку), вузол «classes
» буде опрацьовано в останню чергу, коли всі «classid
» вже опрацьовано.
helper.getValue
— функція яка вертає значення параметра по імені параметра (ми ще її використовуватимемо):
def getValue(node, param): for p in node.params: if p.type == param: return p.value else: return None |
І на цьому кроці ми можемо перевірити, чи встановлено «ceil
«, та встановити його рівним «rate
«, якщо його не встановлено (проте, напевно, краще це робити на етапі генерування коду?).
Досі робимо помилки
Легко запевнитися, що наш модуль ловить всі розглянуті вище помилки :-)
Отже, ми маємо Abstract Syntax Tree для нашої конфігурації, і це дерево «валідне» з лексичної, синтаксичної та семантичної точки зору. Переходимо до Генерування коду.