Ремесло программиста

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » Ремесло программиста » Принципы » Отладка компилятора


Отладка компилятора

Сообщений 1 страница 14 из 14

1

Собственно вопрос, как лучше и проще организовать отладка компилятора? Если транслятор очень элементарно проверяется по грамматике. То с компилятором так не проходит.

Грубо говоря, те тесты которые я за планировал компилятор проходит, но не проходит на системной библиотеке.  На реальной программе было-бы попроще.

2

Декомпозицей очевидно - нужно составлять тесты на отдельные функциональные блоки и тестировать их независимо от всего остального.

3

Проблема в том что у меня 160 функций. И в ручную для них писать тесты проверять а потом устранять ошибки займёт год.
Есть идея сделать фазинг, тогда скорость 1-3 месяца можно управиться. Но беда в том что для него нужна работающая системная библиотека.
Замкнутый круг.
Вчера подставлял Assert'ов для проверки по входу. Из  них ни один не пискнул.  Отсюда вывод, проблема в другом.
А именно в том что у меня куча строк типа:

Код:
Signature:=VarAssignAST.Expression.Factor1.Signature.Designator.ExpressionList.Items[0].Factor1.Signature;

Это я до переменной value пытаюсь добраться.

Код:
x:=SizeOf(value);

В чём проблема каждая точка должна быть экранирована.  Первый экран это грамматический парсер. Второй уровень сам генератор. Но всё равно где-то проверки отсутствуют и компилятор падает.

4

Павиа написал(а):

VarAssignAST.Expression.Factor1.Signature.Designator.ExpressionList.Items[0].Factor1.Signature;

Это код в делфи или на целевом языке?
Если в Делфи, то есть with. А вообще надо как минимум избавиться от повтора "Factor1.Signature".
Упростить доступ функцией с необходимыми параметрами.

5

Проблема в том что у меня 160 функций. И в ручную для них писать тесты проверять а потом устранять ошибки займёт год.

Такова жизнь. Серебряной пули не существует  8-). Поэтому программа и стоит дорого - сложность разработки вырастает не линейно в зависимости от масштаба, соответственно и стоимость ошибки.

VarAssignAST.Expression.Factor1.Signature.Designator.ExpressionList.Items[0].Factor1.Signature;

Объявляете в функции:

a:=VarAssignAST.Expression.Factor1.Signature.Designator.ExpressionList.

И далее

a.Items[0].Factor1.Signature;

Там же все равно ссылки, а ссылочная арифметика быстрая.
А вообще я конечно со своей свиньей в чужую синагогу, но такая вложенность наверно говорит о проблемах архитектуры. Если нужно больше 4-5 точек в обращении к объекту, то надо пересматривать рабочую модель. Если таких строк много, то нужно перепроектирование проводить - зачем объекту высокого уровня брать микроскоп и лезть в кишки объекта более низких уровней? Есть ли такая необходимость? Да еще и как норма, а не исключения.

6

MihalNik написал(а):

Это код в делфи или на целевом языке?
Если в Делфи, то есть with. А вообще надо как минимум избавиться от повтора "Factor1.Signature".
Упростить доступ функцией с необходимыми параметрами.

На, дельфи. на входном языке это банальный SizeOf().
Да в Delphi есть With. Но он предназначен не для этого.
Не всегда возможно упростить. Да и во-вторых изначально архитектура была привязана к грамматикам. Просто что такое грамматика просто и понятно. А какой функционал нужен для генератора не ясно было. Пишешь, не получилось переписываешь. Что-бы не пееписывать было проще передать дерево целиком.
Тем более...
utkin

объекту высокого уровня брать микроскоп и лезть в кишки объекта более низких уровней?

Не всегда знаешь на среднем уровне что понадобиться далее поэтому и передаётся дерево целиком, что-бы было можно понять что куда.
К примеру вызов функции зависит от результирующей переменной. Если та имеет тип структуры, то надо передавать её как параметр.
Или если она реального типа, то должен быть выбран генератор реальных, а не ординарных операторов.

Это всё решается в ведением фазы синтетического анализа. С использованием встроенного языка(IR) от которого по первой хотелось отказаться. Или  создания новых структур с правильными именами, коих не самненно будет больше, но с коими проще работать на этапе генерации. Но в данной версии компилятора это не планировалось.
Что не самоценно будет в следующей версии. 
Хотелось бы сейчас найти или придумать то, что можно по отлаживать простыми тестами.

Отредактировано Павиа (2017-06-16 13:50:36)

7

Не всегда знаешь на среднем уровне что понадобиться далее поэтому и передаётся дерево целиком, что-бы было можно понять что куда.

Значит пишите обертки на высоких уровнях. Например factor1 должен уметь sizeOf(Signature). Items[0] должен иметь свой GetSizeOf, который будет вызывать sizeOf(Signature) и так далее по цепочке. Да методов будет много, но они простые одна-две строки кода и отлаживаются просто. Я прямо вот открываю во вкладках все модули (у меня как правило нижестоящие классы в отдельных юнитах) и если нужно лезть куда в глубь пишу обертку, а потом уже вызываю ее на верхнем уровне.

К примеру вызов функции зависит от результирующей переменной. Если та имеет тип структуры, то надо передавать её как параметр.

Значит в функцию нужно передавать информацию о типе переменной, а функция уже будет селектором вызывать специализированный для каждого типа вариант действий. Знаю муторно и много писать, но по-другому никак.

Хотелось бы сейчас найти или придумать то, что можно по отлаживать простыми тестами.

Базовый принцип ООП инкапсуляция. Если Вы вызываете через 5 уровней так все и будет. Поэтому правило - вышестоящая контора ничего не должна знать о том из чего состоит нижестоящая. Нужна информация пишите геттеры и сеттеры, которые будут обращаться к компонентам. Иначе утоните в реализации (тем более как я понял предполагается что число структур вырастет). Это очень муторно и однообразно писать, но зато со свистом отлаживается.
Я обычно делаю так - пишу каждый класс в отдельном юните. Каждый класс имеет инфраструктуру для обращения к информации которая находится в нем + репорт. То есть отчетная функция, которая формирует TStringList с общим описанием текущего состояния класса. Соответственно вышестоящий Реппорт просто собирает в кучу все нижестоящие отчеты. А потом уже начинается тест - какая-то ситуация и просмотр отчета, чтобы представлять внутреннее состояние объекта при осуществлении тех или иных действий.

8

Павиа написал(а):

А какой функционал нужен для генератора не ясно было. Пишешь, не получилось переписываешь. Что-бы не пееписывать было проще передать дерево целиком.

Это нормально, но потом код приходится упрощать формальными приёмами (см. М.Фаулер).

Павиа написал(а):

Да в Delphi есть With. Но он предназначен не для этого.

Пространство внутри функции настраивать он позволяет. То, что не позволяет, делается дополнительными переменными и методами доступа.

Павиа написал(а):

К примеру вызов функции зависит от результирующей переменной. Если та имеет тип структуры, то надо передавать её как параметр.

Можно создать обобщённый класс или интерфейс.

9

MihalNik
utkin
Это всё хорошо. И я так буду делать, но не сейчас.  Всё это запланировано на следующую версию компилятора.
А сейчас пожалуй лёгкий рефакторинг.

Когда  я только приступал к генератору, я изучал что есть у других. Так вот по скромным подсчётам надо было написать сотню другую классов. И кода не менее 20 тыс строк.
Ау меня сейчас компилятор весит всего 15 тыс строк. И генератор укладывается в 160 функций, а не в 160 классов.

Если по началу не было понятно какие преобразования нужны, то сейчас уже сформированы функции и по ним можно ориентироваться.

Но классы это не панацея. У любого переводчика есть одно свойство он не подчиняется правилам вложенности.
Да можно сделать парсер который вложен.Можно сделать генератор, который так же вложен. Но всё равно останется сердцевина которая не подчиняется вложенности.
И как не крути её так не обработаешь. И её следует закодировать таблицей. Так как таблицу проще контролировать.

10

Павиа написал(а):

Так вот по скромным подсчётам надо было написать сотню другую классов. И кода не менее 20 тыс строк.

Ну, глянул AL-IV, навскидку, 15-20 тыс. строк ядро + на генератор для каждого языка тыс. по 4-8 и библиотечных тыс. по 3 (т.е. тыс. по 7-11 на выходной язык)

Павиа написал(а):

Так как таблицу проще контролировать.

Таблицу очень тяжёло отлаживать, даже для КА одного лексера делать вручную утомительно.

Павиа написал(а):

Да можно сделать парсер который вложен.Можно сделать генератор, который так же вложен.

Зачем их вкладывать? Это лишний слой доступа, проще последовательно расширить, т.е. наследовать.

11

MihalNik
Как только поправлюсь обязательно посмотрю. Но скорее всего вопрос во взаимопонимание. У меня стадия генерация идёт тут же после парсера. И нет промежуточного представления или как-го деления. Как правило в продвинутых компиляторах есть отдельные модули которые реализуют дополнительную обработку.
Интересно как в AL-IV сделано преобразование типов?

MihalNik написал(а):

Зачем их вкладывать? Это лишний слой доступа, проще последовательно расширить, т.е. наследовать

Под вложенностью я имел в виду другое, а именно сюръекцию. Отображение многих к одному.
Когда данные переводятся в более простой вид.
А вот когда надо сделать наоборот отображение одного на многих тут и возникают трудности.
А переводчик вынужден решает неопределённую задачу .Когда у него есть два множиства и куча правил причём часть из них расщепляются,а часть сливаются.
Так как они не однородны, то найти их все и проконтролировать становиться не простой задачью. Как доказать что ваш транслятор работает верно?

MihalNik написал(а):

Таблицу очень тяжёло отлаживать, даже для КА одного лексера делать вручную утомительно.

Лексер? Лексер это тривиальная задача. И легко пишется таблицей и легко отлаживается.  Причём как таблицей так и не таблицей. 
На верно вы имели в виду делать парсер таблицей? Его действительно сложно отлаживать.

Но я не зря сказал о вложенности. Трудности которая присуще грамматическому анализу я решаю через ООП притом довольно просто.  На самом все сложности и-за бестолкового описания. Везде очень тяжёлая теория, а практика достаточна проста, хотя и требует заучить скажем с 10 правил.

Отвлёкся. Трудности которые есть у грамматического анализа остаются в грамматическом анализе. 

Для переводчика нужны таблицы притом очень простые не сложнее регулярных выражений. Будет описываться что искать и во что преобразовывать. От регулярных выражений их будет отличать наличие типа.

Отредактировано Павиа (2017-06-17 21:59:24)

12

Павиа написал(а):

Интересно как в AL-IV сделано преобразование типов?

Нет неявных преобразований переменных, кроме преобразования целого в вещественное.
Нет приведения типов данных.
Все преобразования выполняются явным вызовом функций.

Он же открытый - можно смотреть исходный код.

13

Решил отчитаться что у меня. Нового пока нечего нет.  Отладка грубой силой не помогла. Нужно делать тесты ABI. Для разных типов помноженное на разные способы передачи параметров. А ещё внутренняя структура зависит от вложенности вызовов - вернее не должна зависит, поэтому надо тестировать. 7*5*3=105 функций. И это только STDCALL!
А для этого придётся разработать всё в ручную на ассемблере.  Помимо этого придётся переписать несколько функций в самом генераторе.

14

Павиа написал(а):

7*5*3=105 функций.

что такое 7, 5 и 3 ?


Вы здесь » Ремесло программиста » Принципы » Отладка компилятора