Запутался. С одной стороны, узлы делятся на значения и выражения (которые, в свою очередь, могут содержать другие узлы, являющиеся значениями или выражениями). С другой стороны, выражения и значения делятся по типу возвращаемого значения (логические, числовые, строковые). Соотвественно, не все узлы могут быть дочерними узлами для других узлов. Например, для операции сложения нельзя использовать одновременно строку и число (но можно складывать строку со строкой или число с числом). Какой должна быть иерархия классов, позволяющая на этапе компиляции проверить хотя бы частично, что родительский узел содержит узлы правильных типов? Можно ли обойтись без множественного наследования?
Здравствуйте, Аноним, Вы писали:
А>Запутался. С одной стороны, узлы делятся на значения и выражения (которые, в свою очередь, могут содержать другие узлы, являющиеся значениями или выражениями). С другой стороны, выражения и значения делятся по типу возвращаемого значения (логические, числовые, строковые). Соотвественно, не все узлы могут быть дочерними узлами для других узлов. Например, для операции сложения нельзя использовать одновременно строку и число (но можно складывать строку со строкой или число с числом). Какой должна быть иерархия классов, позволяющая на этапе компиляции проверить хотя бы частично, что родительский узел содержит узлы правильных типов? Можно ли обойтись без множественного наследования?
Почему
для операции сложения нельзя использовать одновременно строку и число ? В некоторых языках можно.
Даже в C++, если где-нибудь есть функция
string operator +(string A,int B);
с какой-нибудь осмысленной семантикой — например переводящая сначала число в строку и добавляющая её к исходной строке.
Вам, скорее всего, нужно строить только дерево разбора, основываясь на приоритетах операций, а типы выводить уже потом, анализируя это дерево и
ища для операторов функции с подходящими сигнатурами, а вот если таковые не найдутся, то диагностировать ошибку.
Здравствуйте, Аноним, Вы писали:
А>Запутался. С одной стороны, узлы делятся на значения и выражения (которые, в свою очередь, могут содержать другие узлы, являющиеся значениями или выражениями). <...> Какой должна быть иерархия классов, позволяющая на этапе компиляции проверить хотя бы частично, что родительский узел содержит узлы правильных типов? Можно ли обойтись без множественного наследования?
А зачем тебе тут вообще какя-то иерархия и наследование?
Что, по сути, должен сделать твой компилятор на этом этапе обраотки текста? Типа разбить на лексемы, собрать лексемы в АСТ и аттрибутировать каждый узел, его типом и параметрами. Так?
Ну, тогда, по идее, у тебя должно быть по классы для каждого типа лексем, ну, там, строковой литерал, целочисленный литерал, идентификатор, ключевое слово, знак операции, литерал числа с плавающей точкой. Ещё можно иметь парные лексемы, которые объединить в одну металексему. Скажем что-то в скобках, оператор языка, имеющий внутри себя несколько кусков кода (ну, типа if/else ветки, case-ветки и т. д.)
Дальше пишешь абстрактный разборщик грамматики, который по статически заданной в данных грамматике рожает дерево таких лексем и мета-лексем. При этом каждый узел параметризован указателем на функцию-обработчик, например, или просто по типу лексемы switch, или это всё наследники абстрактной лексемы, с каким-то виртуальным методом "Компилируй"...
потом берём и так или иначе у вершины дёргаем этот самый метод...
То есть, если это всё собирать в красивый С++, то можно сделать у каждой лексемы статический фабричный метод, возвращающий, например, shared_ptr на абстрактную лексему, а на самом деле строит узел нужного типа по массиву подузлов и текстовому представлению, потом описываешь грамматические правила, ну типа там узел "оператор if" -- строится такой-то порождающей функции и должен сожержать ключ. слово if, выражение в скобках, код, который if, и, факультативно, ключ. слово else и код, который else. Узлы "вырваженеи в скобках", код для if и код для else, помечаем, как нужные, для помещения в дерево и передачи в порождающую функцию.
Ну и типа, так весь язык описываем, и потом запускаем по этим правилам разборщик, который соберёт нам дерево, а у корня позовём метод "компилируй", который рекурсивно всё устроит.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, <Аноним>, Вы писали:
А>Какой должна быть иерархия классов, позволяющая на этапе компиляции проверить хотя бы частично, что родительский узел содержит узлы правильных типов?
Зависит от конкретного языка. К примеру в языках типа Паскаля система типов универсальна, поэтому там лучше все элементы выражений наследовать от одного базового класса. А вот в SQL логические выражения, скалярные, и выражения реляционных отношений практически никак между собой не пересекаются, соответственно и наследовать их от единого типа выражений смысла нет (единственное общее свойство, по сути, это позиция элемента в исходном тексте).
А> Можно ли обойтись без множественного наследования?
Можно. Можно вообще без наследования.
... << RSDN@Home 1.2.0 alpha 5 rev. 100 on Windows 8 6.2.9200.0>>
Здравствуйте, Erop, Вы писали:
E>Ну, тогда, по идее, у тебя должно быть по классы для каждого типа лексем, ну, там, строковой литерал, целочисленный литерал, идентификатор, ключевое слово, знак операции, литерал числа с плавающей точкой.
Вот это как раз совершенно не нужно и даже вредно. Для лексем достаточно просто целочисленного дискриминанта типа лексемы.
... << RSDN@Home 1.2.0 alpha 5 rev. 100 on Windows 8 6.2.9200.0>>