здравствуйте, уважаемые форумчане. занимаюсь самообразованием, и натолкнулся на заковырку...
хочется спросить, насколько оптимально следующее решение:
class CNode {
public:
virtual ~CNode() {}
virtual CValue getValue() const = 0;
virtual void setValue(const CValue &value) = 0;
virtual void traverse() {};
protected:
CNode() {};
};
typedef boost::shared_ptr<CNode> CNodePtr;
class CValueNode : public CNode { //для хранения какой-либо величины
CValue _value;
public:
CValueNode(const CValue &value): _value(value) {};
CValue getValue() const {
return _value;
}
void setValue(const CValue &value) {
_value = value;
}
};
class CVariableNode : public CNode { //для хранения узла "переменная"
CVariablePtr _pVariablePtr;
public:
CVariableNode(CVariablePtr &pVariablePtr): CNode(), _pVariablePtr(pVariablePtr) {};
CValue getValue() const {
if (!_pVariablePtr->initialized()) {
throw CRunTimeException(_pVariablePtr->getName() + " not initialized");
}
return _pVariablePtr->getValue();
}
void setValue(const CValue &value) {
_pVariablePtr->setValue(value);
}
};
//а вот далее мне не нравится...
class CAssignNode : public CNode {
CNodePtr _pVariablePtr;
CNodePtr _pNode;
public:
CAssignNode(const CNodePtr &pVariablePtr, const CNodePtr &pNode): _pVariablePtr(pVariablePtr), _pNode(pNode) {};
void traverse() {
_pNode->traverse();
_pVariablePtr->setValue( _pNode->getValue() );
}
CValue getValue() const { //(1)по логике инструкция присваивания не имеет возвращаемого значения, так как же быть? //do not use it
???// return _pVariablePtr->getValue();
}
void setValue(const CValue &value) { //(2)инструкции присваивания невозможно установить значение (нарушение семантики)
//do not use it
???//_pVariablePtr->setValue(value);
}
};
std::stack( CNodePtr ) _stack;
при синтаксическом разборе каждый построенный узел "инструкции" помещается в _stack. к примеру инструкция присваивания схематически выглядит так:
1. считали переменную, создали для нее нод, запушили (push) его в наш _stack
2. считали оператор присваивания
3. считали выражение, создали для него нод, запушили (push) его в наш _stack
4. считали символ завершения инструкции
5. если все окей, то pop-им запушенные на шагах 1 и 3 ноды из _stack, создаем нод CAssignNode, передав в его конструктор вытащенные из _stack два нода как lhs и rhs узлы, и пушым его в _stack. на д. момент в стеке будет находиться только один нод.
т.е. смысл таков, что и ноды, и сами инструкции оперирующие lhs и rhs "работают" на одном стэке нодов, и в конце разбора текущей инструкции (то бишь, стэйтмента), у нас наш стек будет содержать ровно один нод. таким образом, весь блок возможных инструкций можно представить как class CStatementList : public CNode, что очень прельщает, ибо по завершению синтаксич. разбора у нас в стеке будет лежать лишь _один_ нод -- список стэйтментов, пробежав который, мы тем самым проинтерпретируем программу.
все хорошо, но жутко не нравится выделенные комментами два избыточных метода в CAssignStatement (то что, CAssignStatement не может иметь геттеров и сеттеров, думаю, понятно, отсюда и появление этого топика
.
ежели для семаники "statement" создать отдельный класс, в котором будет только метод void traverse(), то непонятно, каким образом хранить в стэке _stack объекты обоих классов (объектов класса CNode, которые по мере разбора выливаются в объекты CStatement). не хотелось бы злоупотреблять dynamic-cast-ом... что же сделать... завести второй стэк — стэк инструкций, куда закидывать пропарсенные инструкции? душа просит все же как-то все увязать в текущем положении вещей. или же забить что для CAssignStatement (ну и для ему подобных далее) мы вынуждены имплементировать геттер и сеттер (ведь один фик мы не будем их вызывать в ходе своей программы)?
пардон за сумбурность объяснения, но думаю, смысл я передал. кто что посоветует изменить?