Семантичний аналіз

На цьому кроці ми хочемо перевірити нашу конфігурацію на відсутність логічних помилок.

Що савме ми перевірятимемо? Я пропонуж такі тести:

  1. Кожен клас ПОВИНЕН мати визначення «rate«.
  2. Кожен клас ПОВИНЕН мати визначення «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 для нашої конфігурації, і це дерево «валідне» з лексичної, синтаксичної та семантичної точки зору. Переходимо до Генерування коду.

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

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

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