/ЗАВЕРШЁННЫЕ РАЗРАБОТКИ

Разработчик: ЛДО

ПРЕТРАНСЛЯТОР SubJava

Внимание: Это не законченный продукт, а опытная реализации претранслятора. Данное программное обеспечение не прошло полное тестирование! Мы надеемся получить Ваши замечания! Их можно послать Сергею Волотовскому. В настоящее время доступна версия 06 ( от 15 октября 1999 ) ( subjava.zip или subjava.jar).Внимание: Для завершения проекта требуется поддержка спонсоров. Для решения финансовых вопросов свяжитесь с Николаем Казанским.

Введение

При разработке расчетных программ на JAVA (в частности при работе с комплексными числами) сильно страдает наглядность программного кода. Это усложняет процесс разработки и сопровождения программ.
Существенное отличие программного кода от привычного математического языка вызвано отсутствием в JAVA функций и операций между объектами.

Претранслятор SubJava

SubJava - претранслятор, который позволяет использовать следующие возможности:

Наличие этих возможностей позволяет приблизить текст программы к привычному математическому языку или языку другой прикладной области.
Претранслятор преобразует исходный .subjava файл в стандартный .java файл, который может быть откомпилирован любым стандартным JAVA-компилятором.
При преобразовании проверяется синтаксис в объеме необходимом для выполнения преобразования (т.е. ошибки, не влияющие на преобразование, он пропускает).
Претранслятор заменяет перекрываемые операции, функции и глобальные переменные конструкциями, допустимыми в JAVA (т.е. выполняет подстановки аналогично макроопределениям). Аналогично претранслятор выполняет неявные преобразования типов данных при инициализации значений переменных, в вызовах методов или функций (если эти преобразования были описаны).
Исходный текст подобен JAVA текстам, но может содержать операции между объектами, функции, глобальные переменные (константы) и операторы substitutes для указания претранслятору файлов объявлений подстановок допустимых операций между объектами, функций, глобальных переменных и неявных преобразований типов данных.

Оператор substitutions

Операторы substitutions располагаются после операторов import (для каждого файла объявлений подстановок - свой оператор substitutions). Формат оператора:

substitutions <имя файла объявлений>;

Имя файла объявлений подстановок - это java имя (подобно имени класса в операторе import), поэтому оно может быть полным или частичным (с учетом предыдущих операторов import). Если отсутствует файл объявлений, полное имя которого совпадает с заданным именем, то он ищется в пакетах, объявленных в операторах import ( т.е. выполняется попытка найти файл, полное имя которого состоит из имени пакета, объявленного в import, и заданного имени ). Пример:

substitutions forDouble;
substitutions mySubstitutions.forDouble;

Файл объявлений подстановок

Файлы объявлений подстановок реализуются как текстовые файлы с расширением substitutions.
Объявления подстановок (далее объявления) внутри файла идут последовательно в любом порядке.
Объявление может находиться в одной или более строках. В одной строке может быть более одного объявления. Допускается использование любого числа разделительных пробелов.
Файлы объявлений могут содержать комментарии аналогичные комментариям в JAVA-программах (исключение: если комментарий находится в поле шаблона, то он войдет в шаблон, а значит будет вставлен в текст .java файла при применении этой подстановки).
При определении полного имени типа данных (тип операнда или типы аргументов) претранслятор учитывает операторы import, находящиеся в исходном тексте. Это позволяет сделать объявление более кратким (вместо java.math.BigInteger можно BigInteger). 
В настоящее время набор допустимых операторов и их приоритет и ассоциативность определяются стандартом JAVA.
Синтаксис шаблонов объявлений проверяется. В случае ошибки выдается диагностика. Допустимы следующие типы объявлений (приводятся тип объявления, формат и один или два примера):

Использование подстановок претранслятором

Подстановки операторов используются при определении типа результата операции в выражении: требуемая операция с заданными типами операндов (типом операнда для одноместной операции) ищется в списке подстановок операторов и, если не нашли, то тип операции определяется по правилам неявного преобразования в JAVA. Если тип результата операции не удалось определить - выдается сообщение.
Подстановки функций применяются при определении типа возвращаемого значения при вызове метода в выражении: метод с требуемым именем и списком типов параметров ищется в списке методов класса, в списке методов родительского класса, и если не нашли, то в списке подстановок функций; определяются все удовлетворяющие методы (а среди подстановок функции) и затем среди найденных, используя алгоритм самого точного совпадения, удаляются лишние; если осталось более одного метода (подстановки функции), то выдается сообщение об ошибке. При поиске удовлетворительных методов и функций используются подстановки неявных преобразователей типа.
Подстановки глобальных переменных и констант применяются при определении типа переменной в выражении: требуемое имя ищется в списке переменных блока и метода, в списке полей класса, родительского класса и интерфейсов, в списке подстановок глобальных переменных и констант до первого результата (т.е. как только нашли дальнейший поиск прекратили). Если не удалось определить тип переменной - выдается сообщение.
Подстановки неявных преобразователей типа данных используются при инициализации переменных в операторах объявления типа или при преобразовании формальных параметров в фактические: в случае несовпадения типов (инициализируемого и инициализирующего) ищется преобразователь. Если не удалось найти преобразователя - выдается сообщение.

Недостатки реализации и ограничения

На работу претранслятора требуется время, соизмеримое со временем, необходимым для компилятора javac, т.к. проверяется синтаксис (не полностью!), проверяется наличие всех методов, переменных и полей (при определении их типов данных), но основное время занимает загрузка необходимых классов (как классов претранслятора, так и классов, используемых в транслируемом файле).
Хотя в большинстве случаев это несущественно, но необходимо отметить, что все классы, на которые есть ссылки в транслируемом файле, должны быть откомпилированы до запуска претранслятора (в отличие от компилятора javac претранслятор не проверяет дату создания байт-кода классов и интерфейсов).
Претранслятор реализован на JAVA 1.1 (нельзя использовать с JAVA 1.0).
Претранслятор отлаживался в Windows95.

Основные достоинства претранслятора SubJava

Претранслятор позволяет писать программный код на JAVA используя язык прикладной области (например приближая программный код к привычному математическому языку). Он позволяет эмулировать

так как если бы они уже были введены в JAVA без изменений JAVA компилятора или JAVA машины (JVM) (т.е. без изменения спецификации JAVA).
Данная реализация поддерживает все возможности JAVA 1.1:


Претранслятор написан на JAVA, что делает его независимым от программно-аппаратной среды.
Претранслятор вызывается из командной строки, что позволяет использовать его в существующих средах разработки программ (IDE).
Реализована многоязыковая поддержка, что позволяет перевести диагностику на язык пользователя.

Дополнительные достоинства претранслятора SubJava

В программировании давно обсуждается вопрос "где должны находиться методы, реализующие перекрытия операций". В данной реализации сняты все ограничения на местоположение реализующих методов. Это позволяет вводить операции между объектами, не внося изменения в их классы (например, создавая новый класс, содержащий необходимые методы, или как в примере 1 для java.lang.Double описывать все в файле объявлений подстановок).
Данный претранслятор может быть использован при переводе программ из С++ в JAVA. Для этого надо создать файлы объявлений используемых функций, операций между объектами, глобальных переменных и констант, необходимых неявных преобразований типа данных. После чего формировать JAVA программу из текстов программ на С++ и затем обработать полученный текст претранслятором.
При преобразовании структура исходного .subjava файла (отступы, индексы соответствующих строк) по возможности сохраняются в java файле. Это упрощает анализ ошибок, которые обнаружит компилятор JAVA при трансляции .java файла.

Установка и использование SubJava

Внимание: Это не законченный продукт, а опытная реализации претранслятора. Данное программное обеспечение не прошло полное тестирование! Мы надеемся получить Ваши замечания!

В настоящее время претранслятор SubJava реализован как набор java пакетов. Поэтому для установки достаточно развернуть архив (subjava.zip) от одного из корней, указанных в classpath. Т.е. файл SubJava.class должен находиться в каталоге, соответствующем java пакету subjava.* .

Вызов из командной строки:
java subjava.SubJava <необязательные параметры> <имя файла>

Можно использовать jar-архив subjava.jar (в этом случае нет необходимости распаковывать архив).

Вызов из командной строки :
java -jar <имя jar-архива> <необязательные параметры> <имя файла>

<имя jar-архива> - имя архива с указанием пути (например d:\Java\subjava.jar).

<необязательные параметры>:

Параметры могут следовать в любой последовательности.

<имя файла> - имя файла с обязательным расширением subjava. Обрабатываемый файл должен находиться в текущей директории.


Например:
java subjava.SubJava a1.subjava
java subjava.SubJava -errors 10 a2.subjava
java subjava.SubJava -compiler "javac {0}.java" a3.subjava
java subjava.SubJava -output err a4.subjava
java -jar d:\Java\subjava.jar a1.subjava
java -jar c:\Java\subjava.jar -errors 10 a2.subjava
java -jar c:\Java\subjava.jar -compiler "javac {0}.java" a3.subjava
java -jar d:\Java\subjava.jar -output err a4.subjava

Ответы на некоторые вопросы


1. Почему выбрана форма претранслятора к Java?

Противники перекрытия операций утверждают, что использование перекрытий операций приводит к неоднозначности толкования исходного кода, что противоречит идеологии Java. И в некоторых случаях они правы. Введение перекрытий операций в форме претранслятора к Java позволяет устранить этот недостаток (код в *.java, в который будет преобразован код из *.subjava будет иметь однозначное толкование). С другой стороны, реализация в форме транслятора в байт-код потребует разработки и собственной IDE, что достаточно трудоемко. А при реализации в форме претранслятора остается возможность использования любых существующих IDE и компиляторов Java.


2. Можно ли задавать приоритет и ассоциативность для перекрываемой операции?

Авторы не видят необходимости введения такой возможности, т.к. на их взгляд это действительно снижает понимаемость выражений. В настоящее время приоритет и ассоциативность операции определяется стандартом языка Java. Введение этой возможности в принципе осуществимо.


3. Почему претранслятор не имеет аналогов template и enum из С++?

Это первая реализация претранслятора. При следующей реализации эти потребности будут учтены. Хотя в настоящее время разработка этого проекта приостановлена в связи с отсутствием финансирования, поэтому когда это будет, пока неизвестно.


Пример 1


Данный пример демонстрирует введение операций для существующего класса java.lang.Double.

Файл forDouble.substitutions:

convertor      : Double : {0}.doubleValue() :  //  Double -> double
convertor      : double : new Double( {0} ) :  //  double -> Double
  
binaryOperator : Double + Double :
                   new Double( {0}.doubleValue() + {1}.doubleValue() ) :
friendOperator : double + Double : new Double( {0} + {1}.doubleValue() ) :
  
binaryOperator : Double * Double :
                   new Double( {0}.doubleValue() * {1}.doubleValue() ) :
friendOperator : double * Double : new Double( {0} * {1}.doubleValue() ) :
  
binaryOperator : Double - Double :
                   new Double( {0}.doubleValue() - {1}.doubleValue() ) :
binaryOperator : double - Double : new Double( {0} - {1}.doubleValue() ) :
binaryOperator : Double - double : new Double( {0}.doubleValue() - {1} ) :
  
binaryOperator : Double / Double :
                   new Double( {0}.doubleValue() / {1}.doubleValue() ) :
binaryOperator : double / Double : new Double( {0} / {1}.doubleValue() ) :
binaryOperator : Double / double : new Double( {0}.doubleValue() / {1} ) :
  
binaryOperator : double += Double :  {0} += {1}.doubleValue() :

Файл Test.subjava:

substitutions  forDouble;
   
public class Test {
   
   public static void main( String[] args ) {
      Double d1 = 1,  d2 = 5;
      Double d3 = d1 + d2;
      d1 = 5 - d2;
      double d = d3;
      d += ( d * d1 ) / 7;
   }
   
}

Полученный файл Test.java:

public class Test {
   
   public static void main( String[] args ) {
       Double d1 = new Double( 1 ),  d2 = new Double( 5 );
       Double d3 = new Double( d1.doubleValue() + d2.doubleValue() );
       d1 = new Double( 5 - d2.doubleValue() );
       double d = d3.doubleValue();
       d += new Double( new Double( d * d1.doubleValue() ).doubleValue() / 7 ).doubleValue();
   }
   
}

Пример 2


Данный пример демонстрирует введение операций для класса Complex. Аналогично можно ввести функции и операции между любыми объектами.

Файл forComplex.substitutions:

//  substitutions for Complex
convertor      : double : new Complex( {0}) :  //  double -> Complex
   
binaryOperator : Complex + Complex : {0}.add( {1} ) :
friendOperator : double + Complex  : {1}.add( {0} ) :
   
binaryOperator : Complex * Complex : {0}.mul( {1} ) :
friendOperator : double * Complex  : {1}.mul( {0} ) :
   
binaryOperator : Complex - Complex : {0}.sub( {1} ) :
binaryOperator : double  - Complex :
                    new Complex( {0} - {1}.real(), -{1}.image() ) :
binaryOperator : Complex - double  : {0}.sub( {1} ) :
   
binaryOperator : Complex / Complex : {0}.div( {1} ) :
binaryOperator : double  / Complex : new Complex( {0} ).div( {1} ) :
binaryOperator : Complex / double  : {0}.div( {1} ) :
   
name           : IM  : Complex.I :
   
function : sin( Complex ) : {0}.sin() :
function : cos( Complex ) : Complex.cos( {0} ) :


Файл TestForComplex.subjava:

substitutions  forComplex;
   
public class TestForComplex {
   
   static Complex im = IM;
   Complex p1 = sin( im );
   Complex p2 = cos( im );
   
   public static void main( String[] args ) {
      Complex c1 = 1,  c2 = -5;
      Complex c3 = new Complex( 1, 1 );
      Complex c4 = c1 * c2 / c3;
      Complex c5 = 7 / 8;
      Complex c6 = 7 * c2 / 8;
      Complex c7 = sin( c1 ) * cos( c2 ) / c3;
   }
   
}


Полученный файл TestForComplex.java:

public class TestForComplex {
   
   static Complex im = Complex.I;
   Complex p1 = im.sin();
   Complex p2 = Complex.cos( im );
   
   public static void main( String[] args ) {
      Complex c1 = new Complex( 1),  c2 = new Complex( -5);
      Complex c3 = new Complex( 1, 1 );
      Complex c4 = c1.mul( c2 ).div( c3 );
      Complex c5 = new Complex( 7 / 8);
      Complex c6 = c2.mul( 7 ).div( 8 );
      Complex c7 = c1.sin().mul( Complex.cos( c2 ) ).div( c3 );
   }
   
}

Пример 3


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

Файл forMath.substitutions:

//  substitutions for Math

name           : PI  : Math.PI :

function : sin( double )   : Math.sin( ... ) :
function : cos( double )   : Math.cos( ... ) :

Файл TestForMath.subjava:

// example TestForMath

substitutions  forMath;

public class TestForMath {

   static double pi = PI;

   public static void main( String[] args ) {
      double p1 = sin( pi );
      double p2 = cos( 2*pi );
   }

}

Полученный файл TestForMath.java:

// example TestForMath



public class TestForMath {

   static double pi = Math.PI;

   public static void main( String[] args ) {
      double p1 = Math.sin( pi );
      double p2 = Math.cos( 2*pi );
   }

}


От версии к версии


Версия 04 (от 30 июля 1999).


Версия 05 (от 10 сентября 1999).

Претранслятор адаптирован к JDK 1.2.2.

Устранена небольшая неточность в обработке CLASSPATH.


Версия 06 (от 15 октября 1999).

Введена обработка локальных и анонимных классов.

Более корректная работа с path.