Zealint писал(а):
Yoda писал(а):
Потеря переносимости и универсальности в том, что вы не можете взять произвольный plain-text редактор и писать/править исходники.
Почему же не могу? Могу. Просто символ tab будет ошибкой компиляции. На клавиатуре есть символ @, однако факт возможности нажать на него в любом plain-text редакторе вне комментариев не порождает проблему переносимости и универсальности. Нажав на него, программист получит неправильную программу. Так же и с tab.
У табуляции есть особенность. Многие редакторы настроены (или могут быть настроены) на использование табуляции вместо начальных пробелов (или даже в середине текста, если они замещают группу пробелов, заканчивающихся в позиции табуляции). В общем, такова семантика символа, заложенная в стандарте ASCII и его наследниках. Я с бОльшим спокойствием запрещу использование символов вертикальной табуляции (VT) и прокрутки страницы (FF).
Zealint писал(а):
1 Исходные числа, передаваемые в программу, сначала нужно привести к диапазону 0..P-1, а для этого нужно брать их по модулю, и среди них будут отрицательные.
Давайте проанализируем все операции, в которых могут возникнуть отрицательные числа. Насколько я понимаю, единственная такая операция - это вычитание.
Zealint писал(а):
2 Вычитание двух чисел по модулю происходит в два этапа: вычитание и взятие по модулю. Между этими двумя операциями числа могут покидать данный диапазон влево от нуля. Именно поэтому приходится делать, например, так:
Цитата:
с = a - b;
if ( c < 0 )
c += P;
Всё абсолютно верно. Но здесь нет операции остатка от деления!
Zealint писал(а):
Yoda писал(а):
Вообще говоря, невозможно сделать абсолютно защищённый язык. В данном случае я вижу как минимум две проблемы.
1. Как вы планируете наложить соответствующие ограничения? Я не вижу адекватного механизма.
Почти так же как это делается в ООП. Есть класс, от которого можно унаследовать интерфейс (чисто виртуальные функции). Программист может переопределить эти функции как ему вздумается. Другие мы ему запрещаем делать в таких классах. Таким образом, чисто формально программы будут продолжать компилироваться правильно, если мы заменим, например, тип int256 на встроенный.
Во-первых, при замене int256 на встроенный тип программа в любом случае перестанет компилироваться и потребуется ручное вмешательство. Во-вторых, запрет на определение новых операторов в принципе неразумен. Мы же можем определить новые операторы для встроенных типов (например, для той же модулярной арифметики), почему мы должны исключать из этого правила другие классы?
Zealint писал(а):
Поэтому программисту невыгодным станет определять свой тип int256 неправильно. То есть, определить умножение как сложение и наоборот.
Умножение и сложение - это семантика (т.е. реализация), и программисту абсолютно ничего не мешает определить их неправильно.
Zealint писал(а):
Yoda писал(а):
2. Интерфейс в данном случае почти не важен, важна как раз реализация, а её никак ни ограничить, ни проверить.
Реализация может быть любой. Если программист накосячил в реализации или решил использовать операции не по назначению, то он САМ создал себе проблемы.
Так я же об этом и говорю, - мы не можем создать адекватный ограничивающий механизм, программист всегда может
легко накосячить. Поэтому я и постулирую, что программист сам несёт ответственность за реализацию таких расширенных классов. Тем более, что вероятность превращения int256 во встроенный класс исчезающе мала.
Zealint писал(а):
Имеется в виду, что нет типа данных вроде char. Но символьные константы можно использовать при работе со строками. Например, str[j]='A' - так можно. В зависимости от типа str, компилятор преобразует 'A' в число подходящего типа, поэтому в объектном файле вместо 'A' будет, например, 65. Однако написать int8 x ='A' нельзя, так как компилятор здесь совершенно не знает, что за 'A' имеется в виду, то ли это cp1251, то ли UTF-32.
Так вот же оно, противоречие! str[j]='A' (в зависимости от типа str, компилятор преобразует 'A' в число подходящего типа). То есть, str - массив целых чисел и вы одному из них присваиваете символ. В какой кодировке символ? Далее, int8 x='A' – нельзя, т.к. компилятор не знает кодировку. А как же он определяет её в первом случае?
В моей схеме решается так. Кодировка фиксированная – UTF-8. Вторая операция int8 x='A' разрешена, если число, полученное после конвертирования символа, не выходит за диапазон 0...255.
Zealint писал(а):
Как же не часть синтаксиса? str[j]='A', это как раз синтаксис. Квадратные скобки здесь являются синтаксической конструкцией. Как обращаться за постоянное время я предложил: хранить смещение каждого символа строки по отношению к её началу.
str – это что? Массив целых чисел? Если да, то как вы планируете в массиве целых чисел дополнительно хранить смещения? Для такого хранения нужен отдельный, уникальный тип, например, символьный (что противоречит предыдущему утверждению об отсутствии символьного типа). Однако, если вы храните смещения, то для 64-битной системы это будет дополнительно 8 байт на каждый символ, итого получается или структура из 9 байт, или два массива чуть-чуть меньшего размера. Не кажется ли, что представление в UTF-32
гораздо более эффективно, чем такие смещения с извращениями?
Zealint писал(а):
Yoda писал(а):
1. Она не использует глобальных переменных.
А Вам не кажется, что глобальные переменные нужно вообще запретить как недоразумение?
Это невозможно в концепции параллельного мультипроцессорного программирования. Однако ограничить их использование возможно, что я и планирую.
Zealint писал(а):
Функция может получить в качестве параметра статическую переменную, и тогда станет небезопасной.
Вы смешиваете
свойство функции с её
использованием. Безопасность - её внутреннее свойство. Конечно в опасном контексте она может стать опасной, но на самом деле это совсем не важно. Для компилятора важно свойство, а не использование.
Zealint писал(а):
Ещё она может передать сообщение другому процессу ... Если только передача сообщения другому процессу считается безопасной функцией.
Передача сообщения - это
вызов опасной функции. А по определению, функция, вызывающая опасные функции, не может быть безопасной. Тут всё в полном порядке.
Zealint писал(а):
Я не помню, мы всё-таки разрешили inline-assembler? Если да, то функция явно небезопасна.
Нет, у меня inline-assembler запрещён.
Zealint писал(а):
Но даже если нет, всё равно следует небезопасными считать функции, написанные целиком на asm отдельным модулем.
Как я уже говорил, невозможно создать абсолютно защищённый язык, точно также как и невозможно сделать абсолютно безопасный автомобиль. Важно сделать гибкий, мощный, высокопроизводительный язык, который сделал бы опасные моменты
затруднительными и,основываясь на свойствах языка, мог бы легко подсказать программисту, какие конструкции вызывают подозрения.
Zealint писал(а):
Ещё момент: если безопасная функция получила в качестве параметра ссылку не небезопасную функцию?
Если получила ссылку, значит вызвана в опасном контексте. Здесь нет никакого противоречия. В данном случае важно, что компилятор имеет возможность чётко разграничить, где заканчивается безопасность. Смотрите, у нас есть две функции – А и Б. Функция А безопасная. Вы спрашиваете: "А что, если функция Б вызывает А опасным образом?" Отвечаю: из этого следует только то, что функция Б и любые другие, которые вызывают Б, – опасные, но сама функция А от этого не стала опасной. То есть, мы чётко разграничили опасную и безопасную зоны.
Zealint писал(а):
Или безопасные функции должны отличаться каким-то идентификатором при своём определении?
Да, должны. В противном случае компилятору для определения опасности потребуется проверять исходники всех вызываемых функций, что, в общем случае, нереально. Правда я пока не определился, следует ли маркировать безопасные функции или наоборот опасные. Если маркировать безопасные, то мы рискуем постоянно наступать на грабли "забывчивости" программиста, аналогичные спецификации "const" в С/С++. Программисты часто пишут функции типа strlen (char *str); которые не меняет своего аргумента, и вызывает strlen("Hello, World!");, что является ошибкой. Мне кажется, было бы разумней наоборот, явно специфицировать те указатели, по которым
можно менять содержимое, а все остальные считать константными.
Zealint писал(а):
На самом деле очевидно. Стандартный алгоритм анализа скобочной последовательности со стеком совершенно спокойно обнаруживает при получении второй закрывающей скобки, что стек пуст, а мы хотим найти на его вершине открывающую часть. Пары нет, значит предпосмотр вперёд должен отыскать последнюю закрывающую часть И либо отрывающую часть, либо конец файла. Всё, что между ними - код.
Как верно отметил pavia, для реализации этого требуется минимум два прохода. Малое время компиляции - не совсем удачный аргумент, представьте себе, лексический анализ требуется не только компилятору, но и простому текстовому редактору для подсветки синтаксиса. А теперь представьте, что по каждому чиху в
начале файла текстовый редактор должен просматривать весь файл
до последнего символа, даже несмотря на то, что закрывающую пару символов он уже нашёл. Далее запоминать последнюю встреченную пару и начинать подсвечивать символы после неё.
Как быть, если я хочу закомментировать следующий кусок кода целиком?:
Код:
aaa
/* bbb */ */
ccc
/* ddd */ */
eee
И вообще как его парсить? Нужно ли оставлять ccc или начиная с первого комментария сразу пропускать всё вплоть до последнего закрытия? Как бы вся эта каша не превратилась в comment-hell.
Нет, извините, такие комментарии полностью противоречат здравому смыслу, особенно в контексте вложенности.