Automated Interface Code Generation from Ada ...

8 downloads 0 Views 668KB Size Report
automating interface code generation based on Ada package specifications and the ... definition of interface is more general than the mapping between Ada and ...


Automated Interface Code Generation from Ada Specification s Paul R. Pukite DAINA, 4111 Central Ave . NE Columbia Heights, MN 5542 1 (612)781-760 0 [email protected] .edu ABSTRACT: Mapping data structures between incompatible representations remains a n error-prone and tedious part of program development . This letter outlines a method fo r automating interface code generation based on Ada package specifications and the use o f Ada keywords as interface mapping directives . This concept supports information hiding b y placing the details of the generated interface code in the package body .

Introduction For many application programmers, creating code which interfaces incompatibl e sections of software remains a tedious part of the software development process . Often, automated methods have reduced the labor required, especially for tasks such a s development of a graphical user interface or a database interface . Common interfac e development tools in use include graphical programming environments and resourc e compilers. However, in less conventional applications or where no standards exis t [Voketaitis 92], programmers must often depend on their ingenuity and the language , compiler, and operating system constructs available to generate the necessary interfac e code. This letter focuses on the automatic generation of Ada code for interfaces o f specific functionality ; namely those that provide representational control and mappin g between Ada data types and another (perhaps foreign) data type format . In particular, specific systems chosen to demonstrate the utility of the approach include an Ada application program interface to a peripherally attached digital signal processor (DSP ) and an Ada interface to a Prolog interpreter .

Problem Definition The underlying structural model considered is a one-to-one mapping fro m heavily-typed Ada data objects to untyped or weakly-typed foreign entities (the interfac e construction details will of course differ among applications) . In addition, the mappin g could include the transfer of data along a message path or data bus . Note that thi s definition of interface is more general than the mapping between Ada and assembl y language or C code . For these more common applications, Ada compiler pragma's ar e used to handle the conversion invisibly . In this respect, our definition o f representational control refers to the mapping between data types (inter- or intraapplication) which are not supported by the compiler or operating system .

ACM Ada Letters, May/Jun, 1993

Page 74

Volume XIII, Number 3



Sourc e System Obj_Link

Ob j

IO .Source_Link ;

IO .Connect Link ID Source Addr Source:Size

=> => => =>

Targe t Syste m Obj_Link : IO .Target_Link ;

Obj_Link, IO .IDs'FIRST, Obj'ADDRESS Obj'SIZE ) ;

TO .Connect ( Link ID Target_Addr Target_Size

=> => => =>

Obj_Link , IO .IDs'FIRST , Obj'ADDRES S Obj'SIZE ) ;

IO .Transfer(Channel => Obj_Link) ;

Figure 1 . Linear mapping of messages, where source and target are identical in structure . An example of a simple form of interface mapping that does not require much coding effort is the sending of fixed format messages through an I/O channel (bus, seria l port, etc.) . Given that the source and destination objects have the same size an d format, the transfer of the source to the destination can use the protocol shown i n Figure 1 . The connection information is stored in the privately defined Link parameter to be used by the I/O routines located in each system . The identification number is used for routing the message . This method works satisfactorily as the strictly enforced typ e checking of Ada can be bypassed through Unchecked_Conversion or an assemble r routine . A generic packaging into a connection object would also simplify the fina l implementation . More detail on this linear mapping is given in [Campbell 92] . The problem with extending this approach to a more general mapping is that th e objects themselves may be differently defined and/or sized . For example, th e Source_System .Obj may be an Ada defined STRING type while the Target_System .Ob j may be a character buffer with a required ASCII NUL termination . Extra code woul d then be required to intelligently parse the Obj specifications into the foreign forma t before transferring the Obj contents. Note that the use of Ada representational clause s alone will be insufficient in many similar situations . An automatic code generator woul d aid in this respect by determining which components transferred are strings, integers , reals, etc ., thus eliminating any manual intervention . The table in Figure 2 gives examples of mappings that would require such an intelligent transfer of object contents. Based on this general definition and the language constructs available, Ad a should be well-suited for specifying representational control . In fact, the key in creatin g an automatic interface code generator (ICG) through Ada is to use packag e specifications to define the mapping protocol . Subsequently, a YACC or other parser generator tool can lexically analyze, parse, and process the specification to the fina l ACM Ada Letters, May/Jun, 1993

Page 75

Volume Xlll, Number 3



Ada type

Target

Mapping

Character

Word

Target uses a 16-bit little-endian word to represent a character, whereas the Ada source is 8-bit big-endian .

String

Pointer

Copy a string to a buffer location in the Target . Terminate with an ASCII NUL.

Float

Float

Source is 64-bits, Target is 32-bits in a non-IEEE floatin g point format .

Enumeration, String , Integer, Float, etc .

Symbol

Target uses a tagged symbolic format to represent types . Interface must recognize the Ada type and call th e appropriate conversion routine . Symbols can have the same ASCII text representation as the Ada object names .

Record

Record, Structure, or Functor

Each component of the record must be mapped to th e corresponding component in the Target . The component s could be any of the above types .

Array

List

Each component must be added to a list in the Target .

Figure 2 . Examples of specialized mapping protocols . representation . In this way, the CASE tool is embedded in the Ada specification cod e itself, and does not need other documentation (such as an IDL representatio n [Snodgrass 1989]) . As a starting point, the ICG uses the Ada keywords package, procedure, an d function to establish the overall mapping encapsulation and data transfer protocols .

Figure 3 briefly describes the meaning of each keyword . From there, objects and type s that need transformation can be specified . In the following, two applications are presented that demonstrate the propose d code generation methods more concretely . In the last section, the generator design i s described in detail . Example 1. PC-Ada to DSP-C mapping. In this example, a control program is designed in Ada to run on a host PC o r workstation . The system requirements specify an attached DSP to preprocess the dat a provided by an external source . The processed data is then transferred to the host computer from the DSP . In short, the representational control needed is to bilaterall y transfer the data from the DSP working area to the host memory . The data formats used by the DSP [Fuccio 88] include 32 bit floats (in a non ACM Ada Letters, May/Jun, 1 .9.93

Page 76

Volume XI/I, Number 3



Ada keyword

Definition

Mappin g

Package

Interface encapsulation

Package body contains the interface code which i s hidden from the implementor .

Procedure

Query or twoway transfer

Target uses the procedure name as an identifier, accepts data through IN or IN OUT parameters an d returns through the OUT or IN OUT parameters .

Function

Assertion o r storage

Target uses the function name as an identifier, and stores data supplied through IN parameters .

Figure 3. Ada keywords for interface mapping . IEEE standard format) and 16 bit integers. The DSP software is written in non-ANS I C, and compiled with a DSP cross compiler . The challenge is to control the DSP vi a the host Ada program and pass the processed data from DSP to host via a direc t memory access (DMA) protocol - with a limited amount of manual coding . package DSP_I0 i s Max_Data : constant

100 ;

type Data_Array is array ( 1

. . Max_Data ) of FLOAT ;

procedure Get_Data ( Mum Avg : in INTEGER ; Data_Out out Data_Array ) ; end DSP_I0 ;

Figure 4. Specification for Ada/DSP interface . Consider first an Ada package specification for the DSP-to-host transfer a s shown in Figure 4. This package defines a data type Data_Array and a data handle r Get_Data which taken together specifies an abstract interface between the host Ad a program and the peripherally attached processor . The package body to be automaticall y generated supplies the necessary logical translation between the processors . To outline the code generation steps, the DSP_IO specification is first lexicall y analyzed and parsed for interface procedures (any procedure defined within th e specification, such as Get_Data) . Then the base types and directions (IN, OUT, or I N OUT) of the data parameters are determined . In this example, the Num_Avg object is identified as a parameter to be sent to the DSP as a moving average parameter, whil e the Data_Out array will be retrieved from the DSP . Next, the necessary code for th e parameter downloading and uploading is generated by the ICG . ACM Ada Letters, May/Jun, 1993

Page 77

Volume Xlll, Number 3



It is important to note that since the DSP's data areas can be accessed b y symbolic names instead of address, the specification can be simplified by using the sam e naming conventions for the Ada objects . For example, as the DSP executive's symbol table uses the names Num_Avg or Data Array to refer to the respective memory locations, we can use the same names in the Ada specification (remembering capitalization) . During operation, the generator code processes the name, does a lookup in the symbol table for the DSP address, and then uses the address to write o r read from the DSP's memory area. declar e Buffer : DSP o IO .Data Array ; begi n DSP_TO .Get_Data { Num_Avg => 5 , Data_Out => Buffer ) ;

Figure 5. Code fragment calling DSP interface procedure . For this application, a C-language procedure is also needed within the DS P program to run a specific data gathering algorithm using the Num_Avg parameter an d returning the Data_Array buffer. By concurrently generating a skeleton for the foreig n Get_Data procedure, code becomes more readable and maintainable across the syste m boundaries . The code fragment in Figure 5 is a typical call to the Get_Data procedure . Appendix A gives the automatically generated Ada package body that does th e representational translation for this call . Note that the interface procedure follows th e pattern of downloading data, starting the remote algorithm, waiting for the result, an d then uploading data to the host . The package body uses DSP and DMA library routines located in the DSP_Utilities package, which remains hidden from the implementor . The generated C-interface skeleton for accessing the DSP data gatherin g procedure is also shown in the inset . Example 2. Ada to Expert System mapping . Next, an Ada application program that interfaces to expert system shell softwar e is illustrated . The expert system is based on a Prolog-style inference engine an d interpreter (which is constructed from portable Ada [Pukite 92]) . The expert system allows parts of the application program to be interpreted at run-time . The connectio n between the application program and the shell can be either through a static Ada librar y link, a message interface, or a dynamic data exchange (DDE) interface. The representational control needed here is to supply the expert system with data (in th e form of facts) and a query, and then to translate the query result to a data type that ca n be used by the Ada application program . Similar issues occur when interfacing Prolo g to other languages [Boyd 90] . ACM Ada Letters, May/Jun, 1993

Page 78

Volume Xlll, Number 3



package Genealogy i s type Families is ( Cat, Bovine, Primate, Unknown ) ; Query_Error : exception ; procedure Categorize ( Observed : in Answer

STRING ; out Families ) ;

function Belongs_To ( Specie : STRING ; Family : Families ) return BOOLEAN ; end Genealogy ;

Figure 6 . Example of Ada to Prolog interface package specification .

Interpreted Prolog uses a symbolic untyped data format. Incoming symbols ar e recognized and stored internally as string, real, or integer tokens . Obviously, Ada doe s not have semantics for run-time binding built in to the language, thus requiring additional Ada translation code . The challenge here is to pass data between th e strongly-typed Ada application program and the symbolically driven expert system . The interface program must recognize the object being passed and do the bilatera l conversion between Ada objects and the Prolog symbols . Clearly, a way of representing assertions and queries in terms of simple Ada syntax would eliminate much of th e coding and maintenance requirements . % -- Prolog-style rulebase -- ASCII File "Genealog .Pro " belongs_to(lion, cat) . belongs_to(cow, bovine) . belongs to(monkey, primate) . categorize(Species, Family) :- belongs_to(Species, Family) . categorize(_, unknown) . % -- Prolog-style query -- Typical interactive inpu t categorize(Species,Family) ?

Figure 7 . Prolog-style ASCII rulebase and query .

An Ada package specification for a hypothetical Ada-to-Prolog mapping is show n in Figure 6 . This package uses a string input data type, an enumerated data typ e Families, a query handler Categorize, and a fact asserter Belongs_To to abstract an interface between the Ada application program and the Prolog-style rulebase . The goal here is to assert facts into the Prolog knowledgebase and then use the rules previousl y stored there to make inferences and deduce a response (a good reference for Prolo g programming is found in [O'Keefe 90]) . The run-time interpreted rulebase is shown in Figure 7 . ACM Ada Letters, May/Jun, 1993

Page 79

Volume Xlll, Number 3



The logical mapping from the Ada specification to Prolog-style syntax is shown i n Figure 8 . In addition, as the assertion of a fact should be clearly apparent within a program, the dummy procedure Assert (see Figure 9) is provided to signify this action . Ada Functio n

Prolog Fac t

function Belongs T o Specie STRING ; Family Familie s return BOOLEAN ;

-6

Ada Procedure

belongs t o lion , ca t )• Prolog Quer y

procedure Categoriz e Observed in STRING ; Answer : out Familie s ) ;

-b

categoriz e lion , FAMILY )?

Figure 8 . Mapping from Ada to Prolog .

Figure 9 . Fact assertion abstraction .

Once the package body is generated by the ICG, the code fragment in Figure 1 0 can be used as a typical assert and query test case . This is similar to a conventional database access ; after asserting the fact into the rulebase, applying the quer y Categorize allows us to retrieve the Family_Name answer . The fundamental role of the ICG in this example is to map the Ada defined dat a structures to the Prolog interpreted environment . This means that the Prolog functor s "belongs._to( )" and "categorize( )" as well as the symbol "cat" must be referred to b y their ASCII representation . The bookkeeping is done automatically by the interfac e generator. For example, the ICG converts the Ada specification function nam e Belongs_To to a functor "belongs_to( )", the procedure name Categorize to a functor "categorize( )", and the enumerated Cat to a symbol "cat" .

ACM Ada Letters, May/Jun, 19 .93

Page 80

Volume Xlll, Number 3



declar e Family_Name : Genealogy . Families ; begi n Load ( "Genealog .Pro" ) ; Assert ( Genealogy .Belongs_To ( "tiger", Genealogy .Cat ) ) ; Genealogy .Categorize ( "tiger", Family_Name ) ;

Figure 10 . Example code fragment for Ada to Prolog call .

Appendix B shows the package body that will do the representational translation . The body includes code for converting the enumerated data type Families to a symbolic or string format used by Prolog . Comparing to Example 1, note that the quer y procedure follows a similar pattern of downloading data and variables, starting th e Prolog interpreter, and then transferring the variable results when the query finishes . The Prolog interpreter and its library routines are accessed through the Rule_Processo r package, which is also hidden from the implementor . Note that because of Prolog's binding rules, IN OUT parameters are not allowe d in interface procedure specifications, with the OUT parameters representing Prolog variable (capitalized ASCII) symbols . During the Prolog resolution process, th e exception Query_Error defined in the package specification will be raised if a patter n match cannot be determined . This would occur if no names within the rulebase woul d match those of the procedure Categorize and its parameters (i .e. the arity).

Interface Generation Method s The automatic interface generators for the DSP and Prolog interfaces wer e written in Ada. The structures of the two generators is similar, with a common front end parser and specific back-end interface builders (see Figure 11) . Both interface generators use a lexical analyzer and YACC parser written in Ad a (the YACCA parser from Tampere University of Technology, Finland) as a front-end . The YACC grammar input is adapted from the full Ada grammar to handle only Ad a package specifications . This reduces the compiler load considerably . The back-end procedure or interface builder takes the YACCA output token s and generates the code that is specific to the application . Most of the algorithmic complexity at this stage comes from reducing the specification data types to their bas e types of INTEGER, FLOAT, CHARACTER, STRING, etc. This type resolution includes keepin g track of subtypes, record components, and array definitions . Once these data types ar e known, the builder generates code for calling library routines that do data ACM Ada Letters, May/Jun, 1993

Page 81

Volume Xlll, Number 3



transformations (including downloading and uploading), initialization, etc . From the examples in the appendices, note that predefined Ada attributes such as 'SIZE and operations such as array slices are important constructs for code generation . Once created, the generated code performs the application tasks listed in Figure 12 .

Data Flow Diagram for Interface Generato r Ad a Specification

YACCA Parser

-►

Interfac e Builde r

Ada Bod y

1

Library o f Interfac e Routines

(optional ) Foreig n Interfac e Skeleton

Figure 11 . Package bodies are generated by parsing Ada specifications . Then, library routines for data transformation are incorporated where needed .

To improve the interface generator capabilities, standardized access to the hos t compiler's library through an Ada semantics interface specification (ASIS) tool [STAR S 90] could reduce the parsing complexity . Also, if more than one generator is to b e built, the interface builder procedure could be supplied as a generic formal parameter . In specific cases, generic packages such as Enumeration 10 are also useful fo r converting between numerical and text representations (e .g . a generic instantiation coul d have been used in Appendix B instead of the Families_Get conversion function) .

Download Data IN Procedur e Specification

Functio n Specification

Proces s

Download Dat a Proces s

OU T IN OUT

Upload Dat a

Figure 12 . ICG subprograms perform basic interface translation tasks .

ACM Ada Letters, May/Jun, 1993

Page 82

Volume Xlll, Number 3



Summary and Conclusion s The method described in this letter is a declarative approach to generating interface code . This means that the package specification used to abstract an interfac e is sufficent to generate the code which performs the specific representatio n transformation . With the techniques described, the code generator places all require d interface code in the package body, using only the Ada specification and selected keywords as inputs. Moreover, this serves to hide the implementation details from th e rest of the world. From the example applications presented, we have found that Ada as a specification language is well suited for generating interface routines . This is perhap s not too surprising because the Ada language has powerful constructs for definin g parameter mode (IN, OUT, or IN OUT) and accessing attributes (e .g. 'LENGTH and 'SIZE) . The approach described in this letter can be used in many other applications . In general, if the representation transformation can be described in terms of an Ad a package specification, an ICG is probably capable of creating the correspondin g translation code. References [Boyd 90] J.L. Boyd and G.M. Karam, "Prolog in 'C', SIGPLAN Notices, vol.25-7, 1990, p .63. [Campbell 92] J.A. Campbell, "Creating Structure from Linearity in Non-Ad a Interfaces", Ada Letters, Jul/Aug 1992, p .20. [Fuccio 88] M .L. Fuccio et al, "The DSP32C : AT&T's Second-Generation Floating Point Digital Signal Processor", IEEE Micro, December 1988 . [O'Keefe 91] R .A. O'Keefe, The Craft of Prolog, (MIT Press, Cambridge, MA, 1990) . [Pukite 92] P .R. Pukite, J . Pukite, and D .S . Barnhart, "Expert System for Redundancy and Reconfiguration Management", Proceedings of the IEEE National Aerospac e and Electronics Conference, 1992, p .233. [Snodgrass 89] R. Snodgrass, The Interface Description Language, (Computer Scienc e Press, Rockville, 1989) . [STARS 90] "Software Technology for Adaptable, Reliable Systems", Nationa l Technical Information Service #AD A229-349, November 2, 1990. [Voketaitis 92] A .M. Voketaitis, Jr ., "A Portable and Reusable RDBMS Interface for Ada", Ada Letters, Sept/Oct 1992, p .64 . ACM Ada Letters, May/Jun, 1993

Page 83

Volume XI/1, Number 3



APPENDIX A : Generated package body for Ada-DSP interface Generated Package Body with dsp_utilities ; with system ; package body dsp_io i s package du renames dsp_utilities ; execs loaded : integer := 0 ; flag_dsp : du .unsigned ; num avg_dsp : du .unsigned ; data_out_dsp : du .unsigned ;

/* Generated DSP C code * / #include "dsp .h ' #define Max Data 10 0 int Num_Avg ; float Data Out(Max_Data] ; main( )

procedure init dsp i s temp : du .unsigned ; begi n temp du .default_addr ; -- Download the DSP executive . if not du .download exec ( "a .out", execs loaded) the n raise program_error ; end if ; du .run . execs loaded := 1 ; -- Get DSP physical addresses . flag_dsp du .get_addr("flag") ; num_avg_dsp := du .get_addr("Num A v data out dsp := du .get_addr("Data_U u end init_dsp ;

{ WaitUntilFlag() ; /*while(!flag)* / Get_Data(Num Avg, Data Out) ; ConvertlEEE(Max_Data Bata Out) ; ResetToStart() ; /*flag=0*/ ~ }

Get Data(Num_Avg, Data_Out ) int Num_Avg ; float Data_Out[] ; { /*insert processing code here* / }

f

Generated C-interface skeleton used for creating DSP executive " a .out" .



DSP data transfer procedure procedure get_data(num_avg : in integer ; data out : out data_array) i s start : integer := 1 ; begi n if execs loaded = 0 the n init cusp ; end if ; -- Download input parameters . du .download array(num avg dsp, (num_avg'size + 7)/8, num_avg'address) ; Start DSP program ny setting flag . du .download_array(flag dsp, (start'size + 7)/8, start'address) ; -- Wait for DSP to finish, allow other tasks to run . while du .dsp_run_flag(flag_dsp) loo p delay 0 .05 ; end loop ; -- Upload output parameters . du .upload_floats(data_out_dsp, (data_out'size + 7)/8, data_out'address) ; exceptio n when others = > du .halt ; raise ; end get_data ; end dsp_io ;

ACM Ada Letters, May/Jun, 1993

Page 84

Volume XI/1, Number 3



APPENDIX B : Generated package body for Ada-Prolog interfac e Generated Package Body with rule_processor ; package body genealogy i s package rp renames rule_processor ; type string access is access string ; -- Enumeration strings . families array : constant array(families) of string_access (unknown = > new string'("unknown"), primate => new string'("primate"), bovine => ne w string'("bovine"), cat => new string'("cat")) ; -- Enumeration conversion . function families_get(str : string ; last : integer) return families i s begi n for i in families loo p if str(1 . . last) = families_array(i) .all(1 . . last) the n return i ; end if ; end loop ; raise program error ; end families_get ; Prolog query procedure procedure categorize(observed : in string ; answer : out families) i s temp string : rp .symbol ; last : integer ; begi n rp .start fact_input(query => true) ; rp .input_functor("categorize") ; -- Download string . rp .input_symbol(observed) ; -- Download variable . rp .input_symbol("ANSWER") ; rp .end_fact input ; Run query . if not rp .interpret(token_input => true, do_tro => true) the n raise query_error ; end if ; rp .start token_get ; Upload enumeration . rp .get_symbol string(temp_string, last) ; answer := families_get(temp_string, last) ; end categorize ; Prolog database entry function belongs_to(specie : string ; family : families) return boolean i s begi n rp .start fact input ; rp .input`functor("belongs_to") ; -- Download string . rp .input_symbol(specie) ; -- Download enumeration . rp .input ` symbol(families_array(family) .all) ; rp .end_fact input ; -- Assert facts . return rp .interpret(token_input => true) ; end belongs_to ; end genealogy ; ACM Ada Letters, May/Jun, 1993

Page 85

Volume XI/I, Number 3

Suggest Documents