STRUMENTA LANGUAGE ENGINES · IBM EGL

In EGL, get dept; is a SELECT. Nothing in that line says so.

Enterprise Generation Language issues most of its database access through statements that contain no SQL at all. What they mean comes from the record they name, its stereotype and its property block — usually declared in another file. A parse tree tells you almost nothing. Resolution tells you everything.

This engine ships both: an AST module and a semantics module that resolves across the workspace, with tests named after exactly this problem.

The engine at a glance
ERARBD-era and EDT-era EGL
ELEMENTS13 top-level declaration kinds
MODULESAST + semantics
SQLimplicit statements + #sql{}
OUTPUTAST · JSON · XML · LionWeb
RUNTIMEJVM library · CLI

Three tokens, four files

get_statement · open_statement · record · init_braces

EGL is a fourth-generation language whose deliverable is generated code: you write EGL, the toolchain generates COBOL, Java or JavaScript. That design decision is why get dept;, add dept;, replace dept; and open deptSet for dept; carry no SQL text. The generator worked it out from declarations. So must anything that wants to know what the application touches.

What has to be resolved before the statement get dept; means anything The statement names a variable. The variable is declared with a record type. The record type is declared in another file, with a stereotype that says it is an SQL record, and a property block that gives the table names and the key item. Only the whole chain gives the table and the key that the statement reads. get dept; dept Department; record Department DEPARTMENT_TABLE EACH ARROW IS A LOOKUP THAT MAY CROSS A FILE BOUNDARY. the statement the variable type sqlRecord keyItem = id WITHOUT THE RECORD DECLARATION, ITS STEREOTYPE AND ITS PROPERTY BLOCK, THE STATEMENT IS THREE TOKENS AND NO MEANING. THIS IS WHY THE SEMANTICS MODULE MATTERS MORE ON THIS PAGE THAN THE GRAMMAR DOES.

The grammar covers the whole RBD and EDT declaration vocabulary — program, standalone function, library, record, handler, service, interface, delegate, dataItem, dataTable, externalType, enumeration and formGroup, each with its stereotype slot. The SQL-bearing statements are modeled as first-class nodes with their options: forUpdate, singleRow, from, scroll, hold, cursor, noCursor, usingKeys.

But the grammar is the smaller half, and saying so is the most useful thing this page can do. The semantics module resolves record variables to their declarations across files, function calls to their definitions, field references into record structures — and, specifically, the implicit-SQL statements to the records they read. There are test suites named GetStatementResolutionTest, OpenStatementResolutionTest, CrossFileMemberResolutionTest, RecordsFieldsResolutionTest and FunctionResolutionTest. Tests named after the hard problem are better evidence than an adjective.

Explicit SQL exists too, in #sql{ … } blocks with EGL host expressions interpolated as :dept.id. Those blocks are lexed as a single brace-nesting-aware token and preserved verbatim, with the surrounding statement fully structured. There is also #sqlcondition{ }, and prepare … from “…”, which is SQL assembled at run time and opaque to everyone.

One relief after RPG and COBOL, and worth saying plainly rather than pretending all legacy looks alike: EGL is free-format text. No columns, no card image, no indicator area, no EBCDIC archaeology. It also has no preprocessor — no COPY, no include, no macro pass. Reuse is by package and import. All the difficulty moved from preprocessing into cross-file resolution.

Input — EGL with implicit and explicit SQL

SQLBatch.egl, trimmed

package com.CompanyB.CustomerPackage;

program SQLBatch type basicProgram

  dept Department;

  function updateRow()
    set dept empty;
    dept.id = "T100";
    get dept forUpdate;
    if ( dept is noRecordFound )
      sqlFailure();
    else
      dept.description = "Test Engineers";
      replace dept;
      commit();
    end
  end

  function clearTable()
    dept.id = " ";
    execute delete
      #sql{
        delete from DEPARTMENT_TABLE
        where id >= :dept.id
      } for dept;
    commit();
  end

  function sqlFailure()
    writeStdOut ( "SQL code = " + sysVar.sqlData.sqlCode );
  end

end
get dept forUpdate; and replace dept; are a keyed SELECT and an UPDATE with no SQL written anywhere. dept is noRecordFound looks like a comparison and is a record-state test. sysVar.sqlData.sqlCode is a system variable that has to survive into whatever this becomes. Only the #sql{} block contains SQL you could find with a text search.

Output — parse tree, by grammar rule

with the resolutions the semantics module adds

egl_program SQLBatch, stereotype basicProgram
├── dept : Departmentrecord Department.egl
├── function updateRow
│   ├── get_statement dept, forUpdate
│   │   └── resolved → record Department
│   ├── ⋯
│   └── replace_statement dept
├── function clearTable
│   └── execute_statement delete, for dept
│       └── sql_code verbatim, positioned
└── function sqlFailure
These are real rule names in the shipped grammar. The two resolved arrows are the semantics module, not the parser — and they are the reason this engine ships two modules rather than one. The sql_code node holds the #sql{} text with its position; it is preserved, not parsed, by this module.

Four places where EGL is not what it looks like

EGL reads like a tidy modern language, which is precisely the trap. Each of these looks like ordinary syntax and is actually a semantics problem.

Property blocks decide the meaning

The { … } initializer is a union of name-value pairs and bare expressions, nested arbitrarily, attached to declarations, records, fields, forms and variables. It looks like decoration. It carries the SQL mapping, the UI layout, the validation rules and the generation options — and without reading it, a record is a list of names.

record Department type sqlRecord
  { tableNames = [["DEPARTMENT_TABLE"]],
    keyItem = id }
  id          CHAR(3);
  description CHAR(30);
end

A stereotype changes the language

type basicProgram, type textUIProgram, type sqlRecord, type basicRecord, type RUIHandler, type nativeLibrary, type service. The same declaration keyword means different things depending on what it generates: a program generating COBOL for CICS cannot use Rich UI constructs, and a Rich UI handler only makes sense generating JavaScript. The parser gives you the source; the stereotype tells you which language you are actually reading.

program SQLBatch type basicProgram
handler MortgagePortal type RUIHandler
library RateLib type basicLibrary

Record means five different things

An SQL record; a fixed record with level numbers, which is a byte layout in the COBOL sense; an indexed record; a message record; a Rich UI data model. EMBED splices one record’s structure into another. One keyword, five data models, and a migration has to tell them apart before it can generate anything.

record CustRec type basicRecord
  10 custId   CHAR(8);
  10 custName CHAR(30);
  embed AuditTrail;
end

The symbol graph has ragged edges by design

ExternalType declares a Java or JavaScript type to EGL, with its own stereotype and constructors, so the source deliberately references things that are not in the EGL codebase at all. Delegate is a function type. Both are normal EGL and both mean a resolver has to be honest about where the model stops.

externalType GoogleMap type JavaScriptObject
  function setCenter( lat float, lng float );
end
delegate RateCallback( r decimal(7,4) ) end

Three more belong on the list. move a to b byName copies fields between two unrelated record types by matching names, a construct with no equivalent in any target language. try with onException (ex SQLException) is a typed exception hierarchy layered over sysVar.sqlData.sqlCode. And FormGroup and Form are a whole 3270-style screen sub-language that exists only in the TextUI branch.

What resolution actually produces

Here is the whole EGL problem on one screen: the statement, the declaration it depends on, and the facts that only exist once the two are joined.

Two files, and what the semantics module gets from them

SQLBatch.egl + Department.egl

// SQLBatch.egl
  dept Department;
  get dept forUpdate;
  open deptSet for dept;

// Department.egl — a different file entirely
record Department type sqlRecord
  { tableNames = [["DEPARTMENT_TABLE"]], keyItem = id }
  id          CHAR(3);
  description CHAR(30);
  manager     CHAR(6);
end

// resolved
get  → record Department, stereotype sqlRecord
     → table  DEPARTMENT_TABLE
     → key    id
     → fields id, description, manager
     → mode   forUpdate
open → same record, result set deptSet
The engine resolves the reference and gives you the record, its stereotype, its property block and its fields, positioned. It does not emit a SQL string — generating the statement is the generator’s job, not the parser’s, and we are not going to claim otherwise. What you get is everything you need to answer “which tables does this application touch, from which functions, with which keys” — a report that is simply not producible from EGL text.

Coverage, by construct

Element and statement names in the middle column are rules in the grammar we ship. Resolution rows are the semantics module, which is a separate module in the same release.
Construct Grammar rules and tests Status
All top-level declarations program, function, library, record, handler, service, interface, delegate, dataItem, dataTable, externalType, enumeration, formGroup Supported — all 13
Stereotypes on declarations stereotype slots on program, handler, library, record, dataTable, form, externalType Supported
Property blocks init_braces, init_brace_elem Supported — nested, mixed name-value and expression
Implicit SQL statements get_statement, add_statement, replace_statement, delete_statement, open_statement, execute_statement Supported, with their option sets
Explicit SQL blocks sql_code, #sql{}, #sqlcondition{} Captured verbatim and positioned; not parsed here
Cross-file member resolution CrossFileMemberResolutionTest, RecordsFieldsResolutionTest Supported — over a whole codebase
Implicit-SQL statement resolution GetStatementResolutionTest, OpenStatementResolutionTest Supported
Records as byte layouts record_field with level, EMBED Supported
Exception handling and record state try, onException, is noRecordFound, sysVar Supported
VisualAge Generator compatibility move … withV60Compat The compatibility form parses
ExternalType targets in Java or JavaScript Declared and recorded; the target type is outside EGL
Build and deployment descriptors .eglbld, .egldd Not parsed
VisualAge Generator or CSP source Not EGL — a conversion question before a parsing one

What you receive

Two modules, generated documentation, a command-line tool and a person to write to.

01 The engine

An AST module and a semantics module

Two JVM libraries. The AST module parses EGL into a Starlasu tree with a position on every node; the semantics module resolves across the workspace. On EGL, taking the first without the second answers very little — which is the whole argument of this page.

AST module · semantics module
02 The model

A designed AST, serialized or interchanged

Parse-tree-to-AST mapping is a separate layer, so the model is deliberate rather than a renamed parse tree. Serialize to JSON or XML, or interchange through LionWeb so the model travels to tooling we did not write. A command-line tool produces the serialized tree without embedding anything.

JSON · XML · LionWeb · CLI
03 The evidence

Golden trees, and a run against real projects

The regression method is worth knowing before you buy: paired blessed examples, each an EGL source file with its expected AST committed alongside it, so an upgrade that changes the tree fails the build. Plus an extensive check that downloads external EGL projects and parses them — code nobody here wrote.

blessed ASTs · external project run
04 The license

Standard, Distribution or Service — support included

Standard for use inside your own organization, Distribution if the engine ships inside a product you sell, Service if it runs behind a service you operate. Generated HTML documentation of the AST module ships with it, so day one is reading the model rather than guessing at it. A license file is registered once per process and refreshed from our license service, on the same terms as every other engine in the catalog.

three tiers · support included
WHY PEOPLE PARSE EGL AT ALL

Nobody is starting a new EGL project.

Organizations with EGL are looking for a way out — and because EGL generates code, they usually cannot just keep the generated COBOL either. That is the situation this engine is built for, and it is not a situation the page should be coy about.

An exit from a generate-to-target language needs two things, not one. A transpiler that reads the source and writes the target, and a runtime that reproduces the semantics the generator used to supplyget, open, foreach, record state, sysVar. Without the second, the generated Java is a pile of hand-written boilerplate that reimplements EGL badly, once per program.

Both exist here and both are built on this AST: an EGL-to-Java migration pipeline that emits Maven projects, and an EGL runtime library the generated Java depends on. That is a materially shorter path than building a parser and then discovering you need sysVar.sqlData.sqlCode semantics in Java.

If what you want is not a migration but a decision, the same model answers the questions that come first: which tables the application touches and from where, what is Rich UI versus TextUI versus batch, where the ExternalType edges into Java and JavaScript are, and therefore what the exit would actually cost.

What a licensee typically builds

in the order people reach them

01  Inventory
Which tables, from which functions, with which keys — the report that needs resolution, not grep.

02  Dependency map
Programs, handlers, services, libraries, records and delegates, with the ragged edges made explicit.

03  Front-end scope
Rich UI handlers, their events and their external-type bindings: what has to be rebuilt, what is plumbing.

04  The migration
Transpile to Java, on a runtime that reproduces EGL’s own semantics.

Why license this rather than rebuild the front end from EDT

The honest alternative is named and it is real: EDT, the Eclipse EGL Development Tools project, EPL 1.0, which is where EGL went as open source when IBM stopped pushing Rational Business Developer. It is the only public artifact of consequence for this language. Its last release, 0.8.2, was in January 2013.

So the first cost is not licensing, it is archaeology. You would be reconstructing a working grammar from a thirteen-year-old incubating codebase and the RBD reference, and validating it against the source you happen to hold.

Then you would find that the grammar is the smaller half. get dept; is three tokens. Everything a buyer actually wants to know — which table, which key, which fields, from which function — lives in a resolver that reads the record declaration in another file, its stereotype and its property block. There is no .g4 file anywhere that gives you that, and it is not a weekend of work.

Property blocks and stereotypes are a semantics problem disguised as syntax. Getting the { … } union right is the easy part; interpreting it is where a from-scratch attempt stalls, usually after the schedule has already been committed.

And the exit needs a runtime as well as a transpiler. That is the part nobody scopes at the start, and it is the reason EGL migrations that begin with “we will write a parser” tend to arrive at “we are now reimplementing EGL” about four months in.

The method behind our estimates has a name: read the Chisel Method for how we make this work estimable, or Migration Services if the migration rather than the engine is what you need.

THE REST OF THE ESTATE

EGL sits next to the code it used to generate.

The COBOL it generated for z/OS. The Db2 SQL it issues. Sometimes the RPG on the box beside it. An EGL exit that stops at the EGL answers half the question.

Every Strumenta engine is built on Starlasu, so an EGL tree, a COBOL tree and a SQL tree have the same shape, the same traversal model and the same API. One tool walks all of them — which is exactly what a migration or a cross-language inventory needs, and precisely what you cannot get by gluing unrelated open-source parsers together.

For EGL that is concrete rather than abstract: the #sql{} text this module preserves verbatim is parsed properly by a Strumenta SQL engine, and both produce nodes in one model. The tables named in explicit SQL and the tables reached through implicit statements end up in the same inventory.

Every engine also supports LionWeb, so the models interchange with LionWeb-compliant tooling instead of being locked inside one process — and every engine is available through bindings from Java, Kotlin, Python, TypeScript and C#. The host application picks the language; the parser does not.

Engines in the same estate

one model, one traversal

COBOL engine →
The code EGL generated, if you still have to read it.

SQL engine →
What is inside the #sql{} blocks.

Java engine →
The ExternalType edges, and the target you are moving to.

The whole catalog →
15+ engines, one AST framework underneath.

What to discuss before you license

Six things we would rather tell you now than have you find in week three.

  • Implicit SQL cannot be resolved without the records. If you send us the programs but not the Record, DataItem, Library and Interface declarations they depend on, get dept; stays three tokens. The complete workspace is not a nice-to-have on this language; it is the input.
  • The SQL inside #sql{} is captured, not parsed. You get the text and its exact position. Analyzing the statement needs a SQL engine, which is a licensing question rather than a technical obstacle.
  • Build and deployment descriptors are not parsed. .eglbld and .egldd configure generation. If what you need to know lives in them, that is a separate piece of work and we should scope it explicitly.
  • ExternalType targets are outside EGL by construction. A reference into a Java or JavaScript type cannot be resolved from EGL source alone. The engine records the declaration and the reference; where the model stops is visible rather than silently wrong.
  • VisualAge Generator and CSP source are not EGL. If you have pre-conversion VAGen, that is a conversion question before it is a parsing question, and it changes the shape of the project.
  • It runs on a JVM, in your environment. If your toolchain is Python, TypeScript or C#, you use the bindings or the serialized tree rather than the jar in-process — which works, and is worth designing for deliberately rather than discovering late.

What we will ask you

  1. RBD-era EGL or EDT? Which RBD version? Any source still in VisualAge Generator or CSP form?
  2. What do you generate today — COBOL for z/OS CICS, batch or IMS, Java, JavaScript, or several?
  3. How much is Rich UI, how much TextUI with forms and converse, how much batch or service?
  4. Do you have the complete workspace, including every Record, DataItem and Library?
  5. Do you use ExternalType to reach into Java or JavaScript, and how much?
  6. Do you need the SQL inside #sql{} parsed, or only isolated?
  7. Target: analysis and documentation, dependency mapping, or a migration to Java?
  8. Standard, Distribution or Service — will this be embedded in something you ship?

Straight answers

Can you tell us which database tables our EGL application touches?

Yes, and it is the question this engine exists for. Most EGL database access is implicit: get, add, replace, delete and open carry no SQL text, so the answer only appears after the record variable is resolved to its declaration, its stereotype is read and its property block is interpreted — usually across files. The semantics module does that, and there are test suites named after those exact resolutions. What you need to supply is the complete workspace, records included.

Which EGL does it handle — RBD or EDT?

Both eras. The grammar covers the full declaration vocabulary of RBD and EDT EGL — programs, functions, libraries, records, handlers, services, interfaces, delegates, data items, data tables, external types, enumerations and form groups — along with the VisualAge Generator compatibility form of move. What is not EGL is VAGen or CSP source itself; that was converted into EGL, and if you still have it, it is a different conversation.

Is EGL column-positional like COBOL and RPG?

No, and it is worth saying rather than assuming all legacy looks alike. EGL is free-format text with braces and end, ordinary comments, ordinary encoding, and no preprocessor at all — no COPY, no include, no macro pass. Reuse is by package and import. All the difficulty moved from preprocessing into cross-file semantic resolution, which is where this product puts its weight.

Can we modify the grammar?

No. This is a commercial engine and the grammar is not part of what ships. You receive the AST and semantics modules, generated documentation, a command-line tool and the serialized model — and you extend behavior by walking and transforming the AST, which is what Starlasu is built for. If you need coverage the engine does not have, tell us and we will scope it as work rather than hand you a .g4 file.

Can we use it from Python, TypeScript or C#?

Yes. The engine is built on Starlasu and is available through bindings from Java, Kotlin, Python, TypeScript and C#, and it supports LionWeb, so the model interchanges with LionWeb-compliant tooling. If you would rather not embed anything, the command-line tool serializes the tree to JSON or XML and any language can read that.

Send the records, not just the programs on this language it is the records that carry the answer

Send us one EGL workspace, and we will tell you what it touches.

A program or two with the records, data items and libraries they depend on, and we will come back with what parses, what resolves, which tables the implicit statements reach, and where the model runs out at an ExternalType edge. If an exit is what you are scoping, that report is the first page of it.

Scroll to Top