More Free Compilers and Interpreters for Programming Languages ().
They are converted by the semantic translator to a format which depends on the context in which the constant appears. The token table produced by the lexical analyzer contains a single entry for each unique token in the source program. Searching of the token table is done utilizing a hash coded scheme which provides quick access to the table. Each token table entry contains a pointer which may eventually point to a declaration of the token. For each statement, the lexical analyzer builds a vector of pointers to the tokens which were found in the statement.
This vector serves as the input to the parse. Figure 2 shows a simple example of lexical analysis. The parse consists of a set of possibly recursive procedures, each of which corresponds to a syntactic unit of the language.
These procedures are organized to perform a top down analysis of the source program. As each component of the program is recognized, it is transformed into an appropriate internal representation. The completed internal representation is a program tree which reflects the relationships between all of the components of the original source program.
Figure 3 shows the results of the parse of a simple program. Syntactic contexts which yield declarative information are recognized by the parse, and this information is passed to a module called the context recorder which constructs a data base containing this information.
Declare statements are parsed into partial symbol table nodes which represent declarations. The top down method of syntactic analysis is used because of its simplicity and flexibility. The use of a simple statement recognition algorithm made it possible to eliminate all backup. The statement recognizer identifies the type of each statement before the parse of that statement is attempted.
If a statement is not recognized as an assignment, its leading token is matched against a keyword list to determine the statement type. This algorithm is very efficient and is able to positively identify all legal statements without requiring keywords to be reserved. Two modules, the context processor and the declaration processor, process declarative information gathered by the parse.
The context processor scans the data base containing contextually derived attributes produced during the parse by the context recorder.
It either augments the partial symbol table created from declare statements or creates new declarations having the same format as those derived from declare statements.
This activity creates contextual and implicit declarations. The declaration processor develops sufficient information about the variables of the program so that they may be allocated storage, initialized and accessed by the program’s operators. It is organized to perform three major functions: the preparation of accessing code, the computation of each variable’s storage requirements, and the creation of initialization code.
The declaration processor is relatively machine independent. All machine dependent characteristics, such as the number of bits per word and the alignment requirements of data types, are contained in a table. All computations or statements produced by the declaration processor have the same internal representation as source language expressions or statements. Later phases of the compiler do not distinguish between them. A based declaration of the form. Multiple instances of data having the characteristics of A can be referenced through the use of unique pointers, i.
The declaration processor implements a number of language features by transforming them into suitable based declarations. Automatic data whose size is variable is transformed into a based declaration. For example the declaration:. Either or both offsets may be zero. The term “word” is understood to refer to the addressable unit of a computer’s storage. The address of A consists of a pointer to the declaring block’s automatic storage, a word offset within that automatic storage and a zero bit offset.
The word offset may include the distance from the origin of the item’s storage class, as was the case with the first example, or it may be only the distance from the level-one containing structure, as it was in the last example. The term “level-one” refers to all variables which are not contained within structures. The declaration processor constructs offset expressions which represent the distance between an element of a structure and the data origin of its level-one containing structure.
If an offset expression contains only constant terms, it is evaluated by the declaration processor and results in a constant addressing offset. If the offset expression contains variable terms, the expression results in the generation of accessing instructions in the object program.
The discussion which follows describes the efficient creation of these offset expressions. The declaration processor suppresses the creation of unnecessary conversion functions c k and boundary functions b k by keeping track of the current units and boundary as it builds the expression.
As a result the offset expressions of the previous example do not contain conversion functions and boundary functions for A and B. During the construction of the offset expression, the declaration processor separates the constant and variable terms so that the addition of constant terms is done by the compiler rather than by accessing code in the object program.
The following example demonstrates the improvement gained by this technique. The word offset and the bit offset are developed separately. Within each offset, the constant and variable parts are separated. These separations result in the minimization of additions and unit conversions. If the declaration contains only constant sizes, the resulting offsets are constant. If the declaration contains expressions, then the offsets are expressions containing the minimum number of terms and conversion factors.
The development of size and offset expressions at compile time enables the object program to access data without the use of data descriptors or “dope vectors. Unless these descriptors are implemented by hardware, their use results in rather inefficient object code. This code is generally more efficient than code which uses descriptors. In general, the offset expressions constructed by the declaration processor remain unchanged until code generation.
Each subscripted reference or sub-string reference is a reference to a unique sub-datum within the declared datum and, therefore, requires a unique offset.
The semantic translator constructs these unique offsets using the subscripts from the reference and the offset prepared by the declaration processor. The declaration processor does not allocate storage for most classes of data, but it does determine the amount of storage needed by each variable. Variables are allocated within some segment of storage by the code generator. Storage allocation is delayed because, during semantic translation and optimization, additional declarations of constants and compiler created variables are made.
The declaration processor creates statements in the prologue of the declaring block which will initialize automatic data. It generates DO statements, IF statements and assignment statements to accomplish the required initialization. The expansion of the initial attribute for based and controlled data is identical to that for automatic data except that the required statements are inserted into the program at the point of allocation rather than in the prologue.
Since array bounds and string sizes of static data are required by the language to be constant, and since all values of the initial attribute of static data must be constant, the compiler is able to initialize the static data at compi1c time.
The initialization is done by the code generator at the time it allocates the static data. The semantic translator transforms the internal representation so that it reflects the attributes semantics of the declared variables without reflecting the properties of the object machine.
It makes a single scan over the internal representation of the program. A compiler, which had no equivalent of the optimizer phase and which did not separate the machine dependencies into a separate phase, could conceivably produce object code during this scan. The semantic translator consists of a set of recursive procedures which walk through the program tree.
The actions taken by these procedures are described by the general terms: operator transformation and operand processing. Operator transformation includes the creation of an explicit representation of each operator’s result and the generation of conversion operators for those operands which require conversion. Operand processing determines the attributes, size and offsets of each operator’s operands. The meaning of an operator is determined by the attributes of its operands.
This meaning specifies which conversions must be performed on the operands, and it decides the attributes of the operator’s result. An operator’s result is represented in the program tree by a temporary node. Temporary nodes are a further qualification of the original operator. For example, an add operator whose result is fixed-point is a distinct operation from an add operator whose result is floating-point. There is no storage associated with temporaries–they are allocated either core or register storage by the code generator.
A temporary’s size is a function of the operator’s meaning and the sizes of the operator’s operands. A temporary, representing the intermediate result of a string operation, requires an expression to represent its length if any of the string operator’s operands have variable lengths.
Operands consist of sub-expressions, references to variables, constants, and references to procedure names or built-in functions. Sub-expression operands are processed by recursive use of operator transformation and operand processing.
Operand processing converts constants to a binary format which depends on the context in which the constant was used. References to variables or procedure names are associated with their appropriate declaration by the search function. After the search function has found the appropriate declaration, the reference may be further processed by the subscriptor or function processor.
Therefore, references to source program variables are placed into a form which contains a pointer to a token table entry rather than to a declaration of the variable. Figure 3 shows the output of the parse. The search function finds the proper declaration for each reference to a source program variable.
The effectiveness of the search depends heavily on the structure of the token table and the symbol table. After declaration p. The search function first tries to find a declaration belonging to the block in which the reference occurred. If it fails to find one, it looks for a declaration in the next containing block. This process is repeated until a declaration is found.
Since the number of declarations on the list is usually one, the search is quite fast. In its attempt to find the appropriate declaration, the search function obeys the language rules regarding structure qualification.
It also collects any subscripts used in the reference and places them into a subscript list. Depending on the attributes of the referenced item, the subscript list serves as input to the function processor or subscriptor. The declaration processor creates offset expressions and size expressions for all variables. These expressions, known as accessing expressions, are rooted in a reference node which is attached to a symbol table node. The reference node contains all information necessary to access the data at run time.
The search function translates a source reference into a pointer to this reference node. See Figure 5. Since each subscripted reference is unique, its offset expression is unique. To reflect this in the internal representation, the subscriptor creates a unique reference node for each subscripted reference.
See Figure 6. The following discussion shows the relationship between the declared array bounds, the element size, the array offset and subscripts. The virtual origin is the offset obtained by setting the subscripts equal to zero. It serves as a convenient base from which to compute the offset of any array elements. During the construction of all expressions, the constant terms are separated from the variable terms and all constant operations are performed by the compiler.
Since the virtual origin and the multipliers are common to all references, they are constructed by the declaration processor and are repeatedly used by the subscriptor. The declaration:. Array parameters which may correspond to an array cross section argument must receive their multipliers from an argument descriptor. Since the arrangement of the cross section elements in storage is not known to the called program, it cannot construct its own multipliers and must use multipliers prepared by the calling program.
An operand which is a reference to a procedure is expanded by the function processor into a call operator and possible conversion operators. Built-in function references result in new operators or are translated. The declaration processor chains together all members of a generic family and the function processor selects the appropriate member of the family by matching the arguments used in the reference with the declared argument requirements of each member.
When the appropriate member is found, the original reference is replaced by a reference to the selected. The function processor determines which arguments may possibly correspond to a parameter whose size or array bounds are not specified in the called procedure. In this case, the argument list is augmented to include the missing size information. A more detailed description of this issue is given later in the discussion of object code strategies.
It is a three argument function which allows a reference to be made to a portion of a string variable, i. This function is similar to an array element reference in the sense that they both determine the offsets of the reference.
As is the case in all compiler operations on the offset expressions, the constant and variable terms are separated to minimize the object code necessary to access the data.
The compiler is designed to produce relatively fast object code without the aid of an optimizing phase. Normal execution of the compiler will by-pass the optimizer, but if extensively optimized object code is desired, the user may set a compiler command option which will execute the optimizer.
The optimizer consists of a set of procedures which perform two major optimizations: common sub-expression removal and removal of computations from loops. The data bases necessary for these optimizations are constructed by the parse and the semantic translator.
These data bases consist of a cross-reference structure of statement labels and a tree structure representing the DO groups of each block.
Both optimizations are done on a block basis using these two data bases. Although the optimizer phase was not implemented at the time this paper was written, all data bases required by the optimizer are constructed by previous phases of the compiler and the abnormality of all variables is properly determined.
Because of the difficulty of determining the abnormality of a program’s variables, the optimization of those programs which may be optimized requires a rather intelligent compiler. A variable is abnormal in some block if its value can be altered without an explicit indication of that fact present in that block. Future revisions to the language definition may help solve the optimization problem.
The code generator is the machine dependent portion of the compiler. It performs two major functions: it allocates data into Multics segments and it generates machine instructions from the internal representation. A module of the code generator called the storage allocator scans the symbol table allocating stack storage for constant size automatic data, and linkage segment storage for internal static data.
For each external name the storage allocator creates a link an out-reference or a definition an entry point in the linkage segment. All internal static data is initialized as its storage is allocated. Due to the dynamic linking and loading characteristics of the Multics environment, the allocation and initialization of external static storage is rather unusual.
The compiler creates a special type of link which causes the linker module of the operating system to create and initialize the external data upon first reference. Therefore, if two programs contain references to the same item of external data, the first one to reference that data will allocate and initialize it. The code generator scans the internal representation transforming it into machine instructions which it outputs into the text segment. During this scan the code generator allocates storage for temporaries, and maintains a history of the contents of index registers to prevent excessive loading and storing of index values.
Code generation consists of three distinct activities: address computation, operator selection and macro expansion. Address computation is the process of transforming the offset expressions of a reference node into a machine address or an instruction sequence which leads to a machine address.
Operator selection is the translation of operators into n-operand macros which reflect the properties of the machine. A one-to-one relationship often exists between the macros and instructions but many operations load long string, etc. All macros are expanded in actual code by the macro expander which uses a code pattern table macro skeletons to select the specific instruction sequences for each macro.
The length of the object program is minimized by the extensive use of out-of-line code sequences. Although the compiled code makes heavy use of out-of-line code sequences, the compiled code is not in any respect interpretive. The object code produce for each operator is very highly tailored to the specific attributes of that operator. All out-of-line sequences are contained in a single “operator” segment which is shared by all users.
The in-line code reaches on out-of-line sequence through transfer instructions, rather than through the standard subroutine mechanism. We believe that the time overhead associated with the transfers is more than redeemed by the reduction in the number of page faults caused by shorter object programs.
System performance is improved by insuring that the pages of the operator segment are always retained in storage. Each task Multics process has its own stack which is extended pushed upon entry to block and is reverted popped upon return from a block. Prior to the execution of each statement it is extended to create sufficient space for any variable length string temporaries used in that statement.
Constant size temporaries are allocated at compile time and do not cause the stack to be extended for each statement. The term prologue describes the computations which are performed after block entry and prior to the execution of the first source statement. These actions include the establishment of the condition prefix, the computation of the size of variable size automatic data, extension of the stack to allocate automatic data, and the initialization of automatic data.
Epilogues are not needed because all actions which must be undone upon exit from the block are accomplished by popping the stack. The stack is popped for each return or non-local go to statement. If the address of the data is constant, it is computed at compile time. If it is a mixture of constant and variable terms, the constant terms are combined at compile time.
Descriptors are never used to address or allocate data. All string operations are done by in-line code or by “transfer” type subroutinized code.
No descriptors or calls are produced for string operations. The SUBSTR built-in function is implemented as apart of the normal addressing code and is therefore as efficient as a subscripted array reference. A string temporary or dummy is designed in such a way that it appears to be both a varying and non-varying string. This means that the programmer does not need to be concerned with whether a string expression is varying or non-varying when he uses such an expression as an argument. The integer is used to hold the current size of the string in bits or characters.
Using this data format, operations on vayring strings are just as efficient as operations on non-vayring strings. The design of the condition machinery minimizes the overhead associated with enabling and reverting on-units and transfers most of the cost to the signal statement. All data associated with on-conditions, including the condition prefix, is allocated in the stack. The normal popping of the stack reverts all enabled on-units and restores the proper condition prefix.
Stack storage associated with each block is threaded backward to the previous block. The signal statement uses this thread to search back through the stack looking for the first enabled unit for the condition being signaled. Figure 7 shows the organization of enabled on-units in the stack. In these cases, the missing size information is assumed to be supplied by the argument which corresponds to the parameter.
This missing size information is not explicitly supplied by the programmer as is the case in Fortran, rather it must be supplied by the compiler as indicated in the following example:. Since parameter A assumes the length of the argument B, the compiler must include the length of B in the argument list of the call to SUB.
The declaration of an entry name may or may not include a description of the arguments required by that entry. If such a description is not supplied, then the calling program must assume that argument descriptors are needed, and must include them in all cans to the entry.
If a complete argument description is contained in the calling program, the compiler can determine if descriptors are needed for calls to the entry. In the previous example the entry SUB was not fully declared and the compiler was forced to assume that an argument descriptor for B was required.
Since descriptors are often created by the calling procedure but not used by the called procedure, it is desirable to separate them from the argument information which is always used by the called procedure. Since descriptors contain no addressing information, they are quite often constant and can be prepared at compile time.
Mills was responsible for the design and implementation of the syntactic analyzer and the Multics system interface, B. Wolman designed and built the code generator and operator segment, and G. Chang implemented the semantic translator. Valuable advice and ideas were provided by A. The earlier work of M. Copyright and all rights therein are retained by authors or by other copyright holders.
All persons copying this information are expected to adhere to the terms and constraints invoked by each author’s copyright. In most cases, these works may not be reposted without the explicit permission of the copyright holder. I learned Fortran IV in my first semester. Around the same time I started my first programming job, at a small Bethesda company called Moshman Associates. My first task there was to write a macro assembler for the microprocessor.
I wrote a Fortran simulation of the hashing algorithm that we had planned to use for instructions and labels, found that it had an excessively high number of collisions, and was given a nice raise for my trouble. The compiler had many, many options for optimization and for diagnostic output. I spent a lot of time experimenting with the options and carefully inspecting the resulting printouts in an attempt to write the most efficient code possible. I need to explain how we would write and run our code at that time.
We didn’t have our own PCs and we didn’t have terminals to log in to a time-sharing system. Instead, we would use an IBM card punch to punch each line of code into a punched card. The was a complex mechanical device, with noises, rhythms, and so forth. The cards were assembled into a deck, preceded by some job control language JCL statements which provided a name for the job and instructed the computer how to set up input and out devices and how to compile and run the code.
Small decks could be rubber-banded together for safekeeping; larger decks usually for COBOL programs were best kept in the cardboard boxes that originally held the blank, unpunched cards. Once the deck was ready, I would walk up the hall to the job submission window, hand it in to the woman behind the counter, and she would stack it up in the card reader for eventual processing.
At crunch times there would be line of students and a big pile of unprocessed jobs. When it was my deck’s turn to be run, she would load it into the card reader, the computer would read and process the cards, and print the results on a very fast IBM printer.
The attendant would take the printout, wrap it around the cards, and file it away until I came back to the window to collect the results. On a good day the turnaround time would be about 3 to 4 hours. At crunch time it might take slightly longer.
If all went well the printout would include two sections – the evidence of a successful compilation, and the results of actually running the program. I quickly learned to be careful with my code and with my algorithms, so that my code would compile and run after just a few iterations. Others were not so fortunate, and would spend many hours waiting for their results, only to find that they’d misplaced some punctuation, forgotten to declare a variable, or made an algorithmic mistake.
I remember one of my fellow students “bragging” that “I am getting pretty good at this, it only took me 30 tries to get it to compile. I remember taking away a couple of things from these early experiences.
First, there was great value in desk checking your code and your algorithms to increase the odds of a successful run. Second, it was good to have several projects going simultaneously to make the best of your your time. Third, I was always shocked from reading my printouts to see that my code could wait in the queue for several hours in order to be compiled and run in the space of 2 or 3 seconds.
As I mentioned earlier, the IBM line printer had a unique feature known as carriage control. By punching different special characters in the first column you could make the printer do some special things when it printed out your code. This was a good way to make sure that each function was on a page of its own. The next line would overstrike the current line. The instructor asked us to make our final assignment look as pretty as possible. For most people this meant clean comments, good variable names, a clean structure, and so forth.
I decided to go a step further! Because this was a school, they would do their best to get as much use of each printer ribbon as possible. Instead of printing in a solid black color, the printer would usually produce text that was, at best, a medium gray. I did some experimenting, and found that 3 overstrikes would create nice, black text.
After getting my code to work as desired, I set out to use bold highlighting on all of the variable names. This turned out to be easy, although I spent a lot of time on the card punch. Here’s what I did. The comments were free-form, and could flow from one card to the next as desired.
Let’s say that I was writing a simple loop. The compiler saw a DO statement with a very long comment. The DO statement would look like this on the printout:.
Once they realized that it was me one benefit of going to a small school they allowed it to run to completion. Within a year I worked on a project for the National Science Foundation. I wrote a very cool program that would verify the accuracy of grant data, basically adding up the rows and columns to make sure that they matched in the application an inverse spreadsheet. Currently I have little time to work on the pl1gcc project. The more the merrier. It is a huge task to create a compiler, let alone when there is only one active developer me.
The recent availability of a rather larger body of Multics code is doubtless a useful thing from a testing point of view It comes with a linker and samples. It is apparently free if you use it for non-commercial purposes. Update : the site originally linked to here appears to have disappeared, and I can’t find an official replacement. I suppose you can always search for it, but there’s no guarantee that the sites you find are legitimate.
I prefer to list only official sites. It was originally derived from SmartEiffel. The compiler is primarily distributed in source form, although you may be able to get binaries for it from their “apt” repository if you use Debian or Ubuntu Linux. For those not familiar with Eiffel, it is an object-oriented programming language.
It does not have a runtime garbage collector, but manages its memory and resources using a resource acquisition is initialization RAII convention with optional reference counting.
The Go programming language, created by Robert Griesemer, Rob Pike, and Ken Thompson, is a language designed to be suitable for modern systems programming and fast compilation and linking. It incorporates built-in support for concurrent programming with processes that can communicate with each other and garbage collection.
Note that due to a name collision with an earlier programming language called Go! Another thing to note before you rush to write your critical systems with it is that the language appears to be still under development. R is a language and environment for statistical computing and graphics. It is similar to the S language and environment, and some of the code written for S can run unaltered for R although not all – there are differences. R supports “a wide variety of statistical linear and nonlinear modelling, classical statistical tests, time-series analysis, classification, clustering, OpenComal is a free interpreter for the Comal programming language.
According to its creator, the language is similar to Pascal although less restrictive, less cryptic than C and more powerful than BASIC.
It can generate interpreted as well as native code. The compiler runs under DOS. To use the native and optimizing compilers, you will need an assembler. Harbour is a free compiler that handles the Clipper superset of the xBase language the language that originated with dBase. Stack Overflow for Teams — Start collaborating and sharing organizational knowledge. Create a free Team Why Teams? Learn more.
Asked 3 years, 11 months ago. Modified 2 years, 11 months ago. Viewed 4k times. Community Bot 1 1 1 silver badge. Max G. I have developed lot of sytem applications as I worked in a major american grain company.
I will be glad to exchange with people who really liked this language. Add a comment.
pl i – Where can I find a PL/I Compiler for Windows? – Stack Overflow – Account Options
Stack Overflow for Teams — Collaborate and share knowledge with a private group. Create a free Team What is Teams? Learn more. Asked 2 years, 10 months ago. Active 1 year, 9 months ago. Viewed 3k times. Max G. I have developed lot of sytem applications as I worked in a major american grain company. I will be glad to exchange with people who really liked this language. Add a comment. Yet, despite its project-based controls, it remains a financial timesheet with all the controls necessary to fulfill the stringent needs of payroll, human resources, billing and finance.
TimeControl is available both for subscription in the cloud or for purchase for an on premise installation and includes both a browser interface and the free TimeControl Mobile App for iOS and Android devices. Roslyn The. This enables you to access a wealth of information about your code from compilers, which you can then use for code-related tasks in your tools and applications.
Roslyn dramatically lowers the barrier to entry for creating code-focused tools and applications, creating many opportunities for innovation. Using basic Pascal programming, many functions, no need to install multiple steps and integrate some other tools. It runs on Lua chunks that have been compiled with the standard Lua compiler. It requires that debugging information has not been stripped from the chunk. By default, the Lua compiler includes this debugging information.
This program is written in Java. A JAR package is available in the downloads section so you don’t have to compile it. It runs from the command line and accepts a single argument: the file name of a Lua chunk. The decompiled code is printed to the standard output. Here is an example usage of unluac: java -jar unluac. Support for later versions is also good if the code doesn’t use too many gotos. In addition to standard ANSI Common Lisp, it provides an interactive environment including an a debugger, a statistical profiler, a code coverage tool, and many other extensions.
It comes with a solid set of features including those supported in other. NET Framework languages, and assists developers in packaging and deploying applications. Haskell support for Eclipse We extend the Eclipse IDE with tools for development in Haskell, a functional programming language, providing support for a wide range of tools compilers, interpreters, doc tools etc. It comes with a linker and samples. It is apparently free if you use it for non-commercial purposes. Update : the site originally linked to here appears to have disappeared, and I can’t find an official replacement.
I suppose you can always search for it, but there’s no guarantee that the sites you find are legitimate. It does not have a runtime garbage collector, but manages its memory and resources using a resource acquisition is initialization RAII convention with optional reference counting. The Go programming language, created by Robert Griesemer, Rob Pike, and Ken Thompson, is a language designed to be suitable for modern systems programming and fast compilation and linking.
It incorporates built-in support for concurrent programming with processes that can communicate with each other and garbage collection. Note that due to a name collision with an earlier programming language called Go! Another thing to note before you rush to write your critical systems with it is that the language appears to be still under development. R is a language and environment for statistical computing and graphics. It is similar to the S language and environment, and some of the code written for S can run unaltered for R although not all – there are differences.
R supports “a wide variety of statistical linear and nonlinear modelling, classical statistical tests, time-series analysis, classification, clustering, OpenComal is a free interpreter for the Comal programming language. According to its creator, the language is similar to Pascal although less restrictive, less cryptic than C and more powerful than BASIC. It can generate interpreted as well as native code. The compiler runs under DOS. To use the native and optimizing compilers, you will need an assembler.
Harbour is a free compiler that handles the Clipper superset of the xBase language the language that originated with dBase. It is currently under development. The core interpreter is free for all uses, while the version that supports graphics is free only for non-commercial use. Lua is an interpreted procedural language with “data description constructs based on associative arrays and extensible semantics”.
It has dynamic typing, automatic garbage collection, etc.
Iron Spring Software – Project Spotlight
A one-to-one relationship often exists between the macros and instructions but many operations load long string, etc. I graduate with title “Programmer-Technician”. Each segment is a linear address space whose addresses range from 0 to 64K.
Pl/1 compiler for windows free download free
When writing a code in C language, compiling it will be the most important step as the code can be run only after that. There are many C compilers for windows 7 bit available that can be used for this purpose. C compiler for windows 8 can be used on the Windows 8 platform and works the same way as any C compiler for windows free download. Jun 18, · PL/1 for GCC The pl1gcc project is an attempt to create a native PL/I compiler using the GNU Compiler Collection. PL/I is a third-generation procedural language suitable for a wide range of applications including system software, graphics, simulation, text . The pl1gcc project is an attempt to create a native PL/I compiler using the GNU Compiler Collection. The project is looking for more people to join the development and testing. If you want to help speed up the development of a free PL/I compiler please do contact .