Научная статья на тему 'Трансляция фрагментов исходных текстов программ с использованием спецификаций синтаксиса и семантики языков программирования'

Трансляция фрагментов исходных текстов программ с использованием спецификаций синтаксиса и семантики языков программирования Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
374
53
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
МЕЖЪЯЗЫКОВАЯ ТРАНСЛЯЦИЯ / СПЕЦИФИКАЦИЯ СИНТАКСИСА / СПЕЦИФИКАЦИЯ СЕМАНТИКИ / FORMAL LANGUAGE TRANSLATION / SYNTAX SPECIFICATION / SEMANTICS SPECIFICATION

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Саух Антон Михайлович, Хмельнов Алексей Евгеньевич

Рассматривается задача автоматической трансляции фрагментов исходных текстов программ с одного императивного языка программирования на другой. Для решения задачи используются спецификации синтаксиса и семантики языков программирования. Для описания синтаксиса применяются грамматики YACC, для описания семантики языковых конструкций предложен специальный язык. Основным результатом, рассматриваемым в данной работе, является подход, позволяющий находить подходящие текстовые представления для описаний семантики фрагментов программ по спецификациям синтаксиса и семантики языковых конструкций. Необходимым условием для возможности применения пред ложенного подхода является достаточное сходство семанти ки рассматриваемых фрагментов языков программирования.

i Надоели баннеры? Вы всегда можете отключить рекламу.
iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

SOURCE CODE FRAGMENTS TRANSLATION BASED ON PROGRAMMING LANGUAGES SYNTAX AND SEMANTICS SPECIFICATIONS

A problem of automatic source code fragments translation from one imperative language to another is considered in this article. Formal specifications of syntax and semantics are used to solve it. Yacc grammars are used for syntax description and the special language is introduced to describe semantics of language constructions. The main result concerned in this article is an approach to find matching textual representations of program fragments semantics using language syntax and semantic specifications. It is supposed that involved languages and their semantics are close enough.

Текст научной работы на тему «Трансляция фрагментов исходных текстов программ с использованием спецификаций синтаксиса и семантики языков программирования»

УДК 004.4'41

А. М. Саух, А. Е. Хмельнов

Институт динамики систем и теории управления СО РАН

ул. Лермонтова, 134, Иркутск, 664033, Россия Email: saukham@gmail.com, hmelnov@icc.ru

ТРАНСЛЯЦИЯ ФРАГМЕНТОВ ИСХОДНЫХ ТЕКСТОВ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ СПЕЦИФИКАЦИЙ СИНТАКСИСА И СЕМАНТИКИ

ЯЗЫКОВ ПРОГРАММИРОВАНИЯ

Рассматривается задача автоматической трансляции фрагментов исходных текстов программ с одного императивного языка программирования на другой. Для решения задачи используются спецификации синтаксиса и семантики языков программирования. Для описания синтаксиса применяются грамматики YACC, для описания семантики языковых конструкций предложен специальный язык. Основным результатом, рассматриваемым в данной работе, является подход, позволяющий находить подходящие текстовые представления для описаний семантики фрагментов программ по спецификациям синтаксиса и семантики языковых конструкций. Необходимым условием для возможности применения предложенного подхода является достаточное сходство семантики рассматриваемых фрагментов языков программирования.

Ключевые слова: межъязыковая трансляция, спецификация синтаксиса, спецификация семантики.

Введение и постановка задачи

О возможности автоматической трансляции исходных текстов с одного языка программирования на другой программисты мечтают практически с того момента, когда началось применение нескольких языков, т. е. с 60-х гг. XX в. Однако сложность этой задачи настолько высока, что ее решений для большинства пар языков не существует до сих пор. Здесь мы говорим о трансляции, которая должна порождать эффективный, хорошо читаемый код, подобный тому, который мог бы быть получен при портировании программы вручную. Конечно, трансляция с одного языка на другой часто является одним из этапов работы реализации языка программирования, но при этом исходный язык рассматривается как более высокоуровневый, а целевой -как низкоуровневый, и результат трансляции не предназначен для дальнейшей модификации кода вручную. Примеры использования трансляции при реализации языка: C ассемблер, Fortran ^ C ([1]), Java ^ JavaScript (GWT Возможным решением рассматриваемой задачи является создание модуля трансляции для каждого направления трансляции для каждой пары языков 2.

Целью проекта CodeSX (Code Semantics eXplorer), рассмотрению одного из результатов которого посвящена данная статья, является разработка инструментов для автоматизации реше-

1 Google Web Toolkit. URL: https://developers.google.com/web-toolkit/

2 Фадеев Р. В. Разработка и исследование инструментальных средств многоязыковой трансляции: Дис. ... канд. техн. наук. Таганрог, 2005. 266 с.

Саух А. М., Хмельнов А. Е. Трансляция фрагментов исходных текстов программ с использованием спецификаций синтаксиса и семантики языков программирования // Вестн. Новосиб. гос. ун-та. Серия: Информационные технологии. 2013. Т. 11, вып. 3. С. 53-62.

ISSN 1818-7900. Вестник НГУ. Серия: Информационные технологии. 2013. Том 11, выпуск 3 © А. М. Саух, А. Е. Хмельнов, 2013

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

Сам подход основан на использовании спецификаций синтаксиса и семантики языков программирования. В качестве спецификаций синтаксиса применяются грамматики УЛСС 3. Инструмент УЛСС является генератором кода, который по грамматике языка программирования, содержащей вставки команд на используемом для реализации компилятора языке (в оригинале это С, но есть версии УЛСС и для других языков), генерирует код компилятора (подставляя в него фрагменты кода из описания грамматики). Для того чтобы получить готовый компилятор, сгенерированный код требуется скомпилировать. Вставки команд на языке реализации (управляющий код) описывают действия, выполняемые при работе компилятора, и тем самым определяют семантику языковых конструкций. Грамматики УЛСС были выбраны для использования в CodeSX из-за их высокой популярности: для большинства языков программирования можно найти готовые грамматики, которые зачастую и применяются при их реализации.

В отличие от УЛСС в CodeSX при чтении грамматики языка не выполняется генерация кода - вместо этого строится внутреннее представление содержащейся в ней информации, при помощи которой система может выполнять грамматический разбор текстов на описанном языке программирования. При этом в файле грамматики также может содержаться управляющий код для описания семантики языковых конструкций. Для описания семантики определяемого языка в CodeSX разработан специальный язык, позволяющий определять такие характеристики языковой конструкции, как соответствие с узлом абстрактного синтаксического дерева, определяемые ею пространства имен, объявляемые идентификаторы, а также управлять генерацией кода при формировании результата из синтаксического дерева. Язык описания семантики в CodeSX ориентирован в первую очередь на императивные языки программирования. С использованием спецификации синтаксиса и семантики языка программирования система CodeSX может решить ряд задач верификации и анализа кода, написанного на этом языке [2]. При этом спецификации используются в прямом направлении: семантические действия выполняются по ходу чтения текста и анализа полученных деревьев разбора.

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

Другим отличием в остальном подобных языков программирования являются допустимые места объявления переменных. Так, в С-подобных языках переменные могут быть объявлены в любом блоке, а в языке Паскаль - только в начале процедуры. Для нивелирования этих различий предложен специальный механизм обработки областей видимости переменных и контекстов объявления переменных.

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

На самом деле, различия между языками программирования во многих случаях не сводятся к чисто синтаксическим: возможно отсутствие в целевом языке целых подсистем или механизмов, присутствующих в исходном, например, множественного наследования, шаблонов, сборщика мусора, ЯТТ1 и т. д. Для трансляции между такими языками требуется разрабатывать специализированные библиотеки и способы трансляции, позволяющие воспроизвести недостающую функциональность. Подобные задачи не освещаются в данной работе - основным

3 Johnson S. C. Yacc: Yet Another Compiler-Compiler. URL: http://dinosaur.compilertools.net/yacc/

предположением является сходство семантических возможностей рассматриваемых языков программирования.

В связи с этим в статье рассматривается работа лексического анализатора системы CodeSX и синтаксического анализатора; описываются возможности конструкций управляющего кода, среди которых выделяются дополнения, облегчающие работу механизма генерации кода; рассматриваются работа блока семантического анализа, механизм генерации кода; приводятся примеры работы системы на модельных фрагментах C-подобного и Паскале-подобного языка.

Блок лексического анализа

В большинстве случаев YACC-грамматики работают в сочетании с описаниями лексем языка, по которым инструмент Lex 4 генерирует код лексического анализатора. Блок лексического анализа CodeSX построен на основе генератора лексических анализаторов Lex и позволяет трансформировать поток символов текста программы в поток лексем, опираясь на формальную спецификацию лексики языка программирования. В отличие от оригинального генератора не используется компиляция кода анализатора. В состав грамматик Lex входят выражения на языке реализации, выполняемые анализатором после считывания текста лексемы из входного потока. При использовании Lex эти действия описывались обычным кодом на языке C и компилировались вместе с кодом самого анализатора. В CodeSX эти действия описываются на специальном внутреннем языке и могут быть разобраны и исполнены без привлечения внешнего компилятора.

Блок лексического анализа состоит из настраиваемого лексического анализатора, генератора конфигураций и контроллера управляющих выражений. Лексический анализатор представляет собой детерминированный конечный автомат, разбирающий исходный код, подаваемый ему в виде потока символов. Таблица переходов для этого ДКА формируется генератором конфигураций непосредственно в памяти на основе описания лексики целевого языка программирования. Лексика языка при этом описывается как регулярная грамматика, где каждому регулярному выражению сопоставляется действие, которое должен выполнить анализатор, когда встречает во входном потоке соответствующую строку. Сами такие действия, в свою очередь, интерпретируются контроллером управляющих выражений. Контроллер, исполняя управляющие выражения, формирует выходной поток терминальных символов, а также позволяет переключать режимы работы анализатора (формальная спецификация допускает маркирование правил грамматики таким образом, чтобы в разных режимах анализатор обрабатывал разные наборы регулярных выражений).

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

Блок синтаксического анализа

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

Синтаксический анализатор представляет собой конечный автомат с магазинной памятью и способен разбирать поступающий от лексического анализатора поток терминальных символов, строить на его основе синтаксическое дерево и запускать на исполнение код, относящийся к семантической части анализа. Таблицы переходов для автомата строятся генератором конфигураций по алгоритму LALR(1) [2], что позволяет такому анализатору разбирать большую часть контекстно-свободных грамматик и, следовательно, большую часть реально используемых языков программирования.

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

4 The Lex & Yacc Page. URL: http://dinosaur.compilertools.net/

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

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

Возможности управляющего кода

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

• Syntax - выполняется на этапе формирования синтаксического дерева, сразу после того, как был сформирован синтаксический узел. В данном блоке разрешено вызывать унаследованные от Yacc методы синтаксического анализатора (accept, abort, clearLookahead, errLab и errOk), а также атрибутировать синтаксический узел какими-либо значениями.

• Matching - исполняется на этапе сопоставления синтаксических конструкций с семантическими. Код в этих блоках как раз и описывает, с каким семантическим понятием нужно сопоставить полученный синтаксический узел.

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

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

Выполнение различных блоков происходит следующим образом.

• Сначала, по мере обнаружения конструкций синтаксическим анализатором, исполняются блоки Syntax и Matching. С точки зрения структуры синтаксического дерева это происходит от листьев к корню, т. е. самый крупный нетерминал, например «программа целиком», будет сформирован последним и его блоки Syntax и Matching тоже будут выполнены последними.

• Затем, после того как синтаксическое дерево полностью построено, происходит его обход от корня к листьям и выполнение блока Resolving, причем внутри этого блока, если он объявлен для правила, нужно явно запрашивать исполнение блока Resolving дочерних элементов, что позволяет управлять порядком исполнения.

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

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

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

Блок семантического анализа

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

За выбор конструкции, с которой должен быть сопоставлен конкретный синтаксический узел, отвечает управляющий код блоков Matching. Семантическая конструкция может иметь аргументы, которые сами могут являться семантическими конструкциями. Например, семантическая конструкция «математическая операция» может иметь такие аргументы, как «операция», «первый операнд» и «второй операнд» (первый принимает значения «+» и «-», остальные принимают в качестве значения другие семантические конструкции). Таким образом, на основе связи родитель - аргумент формируется дерево семантики, которое каждым своим узлом связано с каким-то узлом в дереве синтаксиса.

Приведем более наглядный пример. Пусть есть некоторая грамматика, в которой определены нетерминалы expr, statement, statements и lexem (рис. 1), а на вход анализатора подается следующий текст:

A = 15 + 10 * (23 - 35); B = -A + 123;

Тогда этот текст будет разобран в синтаксическое дерево, представленное на рис. 2, которому будет сопоставлено семантическое дерево, представленное на рис. 3.

expr

: expr '+' expr {Matching: {$$ <-> new #Plus(first: #1, second: #3);}}

| expr '-' expr {Matching: {$$ <-> new #Minus(first: #1, second: #3);}}

| expr '*' expr {Matching: { $$ <-> new #Mult(first: #1, second: #3);}}

| expr '/' expr {Matching: { $$ <-> new #Div(first: #1, second: #3);}}

| '(' expr ')'{Matching: { $$ <-> #2; }}

| '-' expr {Matching: { $$ <-> new #UnaryMinus(first: #2); }} %prec UMINUS

| lexem {Matching: { $$ <-> #1; }};

statements : statement {

Syntax: { $$.MarkAsList(element: $1); } Matching: { $$ <-> new #List(element: #1); } Generation: { WriteLine($1); }

}

| statements statement {

Syntax: { $$.MarkAsList(parent: $1, element: $2); } Matching: { $$ <-> new #List(parent: #1, element: #2); } Generation: { Write($1); WriteLine($3);

}};

statement : ID '=' expr ';' {

Matching: { $$ <-> new #Assignment(id: #1, value: #3); } Resolving: {

var %id = Context.Lookup(name: $1.lexVal); if (%id == undefined) Context.Add(

name: $1.Get(name: "value"),

val: new Variable(type: $1.Get(name: "value"))

);

}}; lexem

: NUM {Matching: { $$ <-> new #Literal(lexVal: $1.lexVal); }} | ID {

Syntax: { $$.Set(value: $1.lexVal); }

Matching: { $$ <-> new #VariableAccess(lexVal: $1.lexVal); } Resolving: {

var %id = Context.Lookup(name: $1.lexVal); if (%id == undefined)

Error(message: "Undeclared identifier usage"); else

%id.RegisterUsage(target: ##);

}};

Рис. 1. Пример описания синтаксиса и семантики выражений

Semantic Trees

sample .bd 15 semantic entities

root : List É- parent : Lift

□ element : .Assignment (- ■■ value : Plus first : Literal

- ■■ second : Mult -¡rst : Literal Ë- second : Minus first : Literal second : Literal - element : Assignment É- value : Plus

[-'■■first : Unary Minus

first : VariableAccess second : Literal

Рис. 2. Пример синтаксического дерева Рис. 3. Пример дерева семантики

Из рассмотренного примера видно, что построение синтаксического дерева происходит автоматически, по описаниям терминалов и нетерминалов (в качестве названий узлов синтаксического дерева непосредственно используются имена нетерминалов грамматики). Управление на данном этапе требуется только для преобразования рекурсивных списков в линейные, как показано в блоках Syntax управляющего кода обоих правил для нетерминала statements.

А вот для построения семантического дерева уже требуется управление - для каждого правила указаны блоки Matching, в которых и отмечается, какой узел синтаксического дерева требуется образовать и сопоставить. Если этот блок в управляющем коде правила не указывается, то поведением по умолчанию является ассоциация синтаксического узла с семантическим узлом первого элемента правой части правила ($$ <-> #1).

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

Такая стратегия разрешения и миграции объявлений требуется для того, чтобы реализовать трансляцию между языками, в которых действует разная политика для определения места в структуре программы, где разрешено объявлять идентификаторы. Например, в языке Pascal

объявления переменных разрешены только в блоках VAR, записываемых перед телом подпрограммы, тогда как в C-подобных языках объявлять переменные можно прямо в блоках кода до их первого использования.

В вышеприведенном примере такая работа с контекстами, разрешением и объявлением переменных показана в блоках Resolving управляющего кода для нетерминалов statement и lexem.

Генерация кода

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

На каждом этапе генерации рассматриваются два элемента: текущий узел семантического дерева, код для которого нужно сгенерировать, и последний построенный узел синтаксического дерева. Генерация начинается с корня синтаксического дерева и начального нетерминала грамматики.

На каждом шаге для текущего нетерминала выполняется поиск всех правил грамматики, в которых этот нетерминал находится в левой части правила (рис. 4). Набор найденных правил фильтруется и сортируется. Сначала отбрасываются все правила, результирующий узел в которых сопоставляется с семантическими конструкциями, не соответствующими текущей рассматриваемой. Затем в начало очереди рассмотрения помещаются все правила, формирующие узел, непосредственно соответствующий искомому (блок Matching имеет вид "$$ <-> new #Name(params);", где Name - искомая семантическая конструкция и набор параметров соответствует набору дочерних элементов). После них в очередь помещаются все транзитные правила. Транзитными будем называть правила, в которых не происходит создания нового семантического узла: в них синтаксический узел связывается с семантическим узлом, уже сопоставленным с каким-то его дочерним узлом (т. е. блок Matching имеет вид "$$ <-> #N", где N - номер элемента в правой части правила). Полученная очередь правил рассматривается последовательно, и для каждого нетерминала правой части правила рекурсивно производится попытка сопоставления с аргументами рассматриваемой семантической конструкции.

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

Сопоставить (SN /*узел семантического дерева*/, NT /*нетерминал*/): RL.Очистить(); // Список правил-кандидатов По всем правилам R где левая часть == NT:

Если (R образует SN ИЛИ R транзитное /* $$ <-> #N */) то: RL.Добавить^);

RL.Сортировать({Сначала правила, формирующие SN, потом транзитные}); По всем элементам R из RL: Если R транзитное то

Res := Сопоставить^^ R.ЦельТранзита()); Если Res не nil то: Вернуть Res; Иначе

RCS.Очистить(); //Список преобразованных дочерних синтаксических узлов RCS.Добавить(R.СгенерироватьДочерниеСинтаксическиеУзлы()); По всем CS из дочерних элементов SN:

Childlndex := R.СоответствующийУзел(CS); Child := Сопоставить^, RCS[ChildIndex]); Если (Child.Успешно()) то

RCS[ChildIndex] := Child; Иначе

Проверить следующее R; Res := СоздатьСинтаксическийУзел(Имя: R.ЛеваяЧасть, Дети: RCS); Вернуть Res; Вернуть nil;

Конец Сопоставить;

Рис. 4. Псевдокод процедуры сопоставления правила узлу семантического дерева. Процедура «Сопоставить» возвращает построенное дерево или nil при неудаче

В результате получается синтаксическое дерево (не абстрактное, со всеми необходимыми терминалами), соответствующее спецификации целевой грамматики. Генерация кода по такому дереву - тривиальная задача, однако в ней есть свои подводные камни. В частности, одной из проблем является генерация сложных терминалов, описание которых содержится в спецификации лексики языка. На данный момент эта проблема решается использованием секций Generation управляющего кода.

Пока предложенный алгоритм работает исходя из предположения, что для каждой конструкции правило будет найдено, т. е. обрабатываемые языки обладают сходной семантикой. В дальнейшем планируется использовать спецификации эквивалентных преобразований семантических конструкций для расширения возможностей поиска правил трансляции. Например, при отсутствии в целевом языке подходящего эквивалента для оператора for он может быть представлен в семантике через более низкоуровневые операторы и, в конечном счете, преобразован в код с циклом while.

Примеры работы транслятора

В качестве материала для тестирования CodeSX использовались два модельных языка программирования (C-like и P-like). В основе первого лежит синтаксис языка C#, в основе второго - языка Pascal. Для тестирования алгоритма генерации кода кроме трансляции исходных текстов с одного языка на другой применялась генерация кода на том же языке по прочитанной семантике.

Рассмотрим пример кода на C-like (рис. 5). Результат трансляции с C-like языка на C-like для этого текста показан на рис. 6, а результат трансляции с C-like языка на P-like - на рис. 7. При обратной трансляции полученного текста с P-like на C-like результат получается такой же, как при преобразовании C-like в С-like (см. рис. 6).

{

int a, b; a = 10;

b = 22 + a * 12; int c;

c = a+b*(19 - 23); if (a > b) { c = a*b; int q; q = 23 + c;

c = q - 1; } else c = a;

{

int a, b, c; a = 10;

b = 22 + a * 12; c = a + b * (19 - 23); if (a > b) { int q; c = a * b; q = 23 + c;

c = q - 1; } else c = a;

Рис. 5. Пример кода на C-like

Рис. 6. Результат трансляции с языка C-like на C-like

VAR a, b, c, q: int; BEGIN a := 10;

b := 22 + a * 12; c := a + b * (19 - 23); if a > b then BEGIN

c

q

c

END else c : =

END.

a * b; 23 + c; q - 1

}

}

a

Рис. 7. Результат трансляции с языка C-like на P-like

На данном этапе разработки основным наблюдаемым результатом является корректная миграция объявлений переменных, что можно проследить на примере объявлений a, b, c и q. Такой эффект достигает потому, что в языке P-like единственным образуемым контекстом является глобальный контекст программы, а конструкция объявления переменных допустима в единственном месте:

file : declarations semicolons compound_statement DOT {

Syntax: { Accept(); }

Matching: { $$ <-> new #Program(code: #3, declarations: #1); }

Resolving: {

var %context = new LocalContext();

##.Set(context: %context);

Context.Push(context: %context);

$1.Process();

$3.Process();

Context.Pop();

}

Generation: { WriteLine($1, ";"); WriteLine($3, "."); }

} ;

В C-like любой блок с фигурными скобками образует свой собственный контекст, причем объявления разрешены везде, основным условием является объявление переменной до ее использования:

block

: '{' '}' {Matching: { $$ <-> new #Block(); } } | '{' statement_list '}' { Matching: { $$ <-> new #Block(statements: #2); }

Resolving: {

var %context = new LocalContext(); ##.Set(context: %context); Context.Push(context: %context); $2.Process(); Context.Pop();

}

Generation: { WriteLine($1); IncIndent(); WriteLine($2); DecIndent(); Write($3);

} };

Таким образом, при перемещении переменных для языка P-like все объявления помещаются в начале программы, а для C-like языка объявления переменных перемещаются в начало самого глубокого блока, покрывающего область использования этих переменных.

Заключение

На данный момент разработано программное средство, позволяющее производить трансляцию исходного кода с одного модельного языка на другой (для примера используются два языка, один из которых похож на Pascal, а другой - на C) с применением формальных спецификаций их синтаксиса. Возможности языков пока очень ограничены, так же как и возможности транслятора, но базовая функциональность реализована.

Существенным преимуществом предложенного подхода к реализации транслятора, основанного на использовании спецификаций синтаксиса и семантики отдельных языков программирования без необходимости создания специального кода для трансляции между конкретной парой языков, является значительно более низкая трудоемкость: для системы из n языков и возможности трансляции каждого в каждый требуется подготовить не n*(n - 1) спецификаций, а только n. Иными словами, добавление спецификации нового языка, совместимого по семантике с уже имеющимися, обеспечит реализацию всех трансляторов между новым и уже имеющимися языками.

Дальнейшим направлением исследований представленного подхода к трансляции является разработка механизмов, позволяющих снизить требования на сходство семантики языков и довести работающие примеры до масштаба настоящих языков программирования.

Список литературы

1. A Fortran-to-C Converter / S. I. Feldman, D. M. Gay, M. W. Maimone, N. L. Schryer. Comp. sci. tech. rep. № 149. AT\&T Bell Laboratories. 26 p.

2. Саух А. М. Анализ некоторых семантических аспектов исходных текстов программ на основе формальных спецификаций синтаксиса и семантики // SIBECRYPT-12. Прикладная дискретная математика. Иркутск, 2012. № 5 (прил.). С. 110-111.

3. Хопкрофт Дж., Мотвани Р., Ульман Дж. Введение в теорию автоматов, языков и вычислений = Introduction to Automata Theory, Languages, and Computation. М.: Вильямс, 2002. Гл. 5: Контекстно-свободные грамматики и языки.

Материал поступил в редколлегию 03.06.2013

A. M Saukh, A. E. Hmelnov

SOURCE CODE FRAGMENTS TRANSLATION BASED ON PROGRAMMING LANGUAGES SYNTAX

AND SEMANTICS SPECIFICATIONS

A problem of automatic source code fragments translation from one imperative language to another is considered in this article. Formal specifications of syntax and semantics are used to solve it. Yacc grammars are used for syntax description and the special language is introduced to describe semantics of language constructions. The main result concerned in this article is an approach to find matching textual representations of program fragments semantics using language syntax and semantic specifications. It is supposed that involved languages and their semantics are close enough.

Keywords: Formal language translation, syntax specification, semantics specification.

References

1. A Fortran-to-C Converter / S. I. Feldman, D. M. Gay, M. W. Maimone, N. L. Schryer. Comp. sci. tech. rep. № 149. AT\&T Bell Laboratories. 26 p.

2. Sauh A. M. Analiz nekotoryh semanticheskih aspektov ishodnyh tekstov programm na osnove formalnyh speci-fikaciy sintaksisa i semantiki // SIBECRYPT-12. Prikladnaya diskretnaya matematika. Irkutsk, 2012. № 5 (pril.). S. 110-111.

3. Hopkroft Dzh., Motvani R., Ulman Dzh. Vvedenie v teoriu avtomatov, yazykov i vychisleniy = Introduction to Automata Theory, Languages, and Computation. M.: Vilyams, 2002. Gl. 5: Kontekstno-svobodnye grammatiki i yazyki.

i Надоели баннеры? Вы всегда можете отключить рекламу.