No, not in the compiler, it is documentation for reference. To code the code_gen, I often need to know what attribute fields has a certain node type. It is not question to look over IDL classes definitions to each time determine the inherited attributes of a node ; there are more than 2 hundreds of node types and nearly as many attributes, I would go completely nuts ! So I extracted the developed nodes definitions from the IDL description of DIANA and put them in text documents for reference (14 pages of three columns text for developed nodes definitions).
In fact, as I progressively get accustomed to the DIANA system, I mainly use the DIANA graph printout of the work file $$$.TMP, attributes are mentioned in it, so another reference is in fact not so useful. But at the beginning I had difficulties to read the graph text dump, so the detailed node structures helped me.
It is something, for sure. By the way, I begin to understand some at first apparent oddities of the Rational R1000 which are described in the patents. Their various stores or stacks VAL TYP and so on get some signification when you rub your brains against code_generation from DIANA.
Using DIANA is difficult not because Ada 83 has no object programming features, DIANA is difficult because it has hundreds of elements and its IDL expression uses inheritance of attributes. Use of inheritance is most of the time obscuring things, it is a mathematical way of compact expression for structural constraints of an abstract type, but it is not at all handy.
Using inheritance in a programming language does not simplify things, some Smalltalks introductions underlined this fact : you have to browse the hierarchy to get used to methods inheritance before using the system with profit.
In the M.Ciernak thesis code, there are 21 pages of developed DIANA nodes definitions in Pascal. An OO language would probably allow for a more compact expression, which does not mean it would be simpler to use (I guess it would be in fact worse).
By the way, I did some work on the english documentation on the Framagit ada-83-compiler-tools project. I would be interested in comments about the Readme.md and the few markdown files linked with it.
No! Stop! Bad programmer!
That is EXACTLY the realm where OOP is useful, even the rather hardcore anti-OOP crowd will acknowledge this, and exactly for the reason that you stated; consider the definition from page 22 of te reference manual: EXP ::= leaf | tree ; and further down the page tree => op: OPERATOR, left : EXP, right: EXP ; — assuming the following:
An enumeration, Classification, of the classification of the various DIANA attributes (Lexical,Structural, …, Code, Experimental);
An abstract tagged type Abstract_Base, with a Class discriminate of the above enumeration;
An instance of Ada.Containers.Indefinite_Multiway_Tree( Element_Type => Abstract_Base'Class ), let’s call it DIANA_2020.Tree_Package;
An secondary OOP hierarchy for attribute-types (AT_Boolean, AT_Integer, […], AT_Sequence, AT_Name) defined in Attribute_Package, which package contains a generic for producing a derived type with a field of an instance of Ada.Containers.Vectors of the given type;
— so that: DIANA_2020.NODES.ADS
With
Ada.Streams,
Ada.Tags.Generic_Dispatching_Constructor,
Diana_2020.Attribute_Package,
Diana_2020.Tree_Package;
Package DIANA_2020.Nodes is
Type Source_Position is new Attribute_Package.BASE
with null record; -- It's a stub-for the example.
-- Subtype renaming.
subtype Stream_Parameters is Ada.Streams.Root_Stream_Type'Class;
Type Base(Class : Diana_2020.Classification) is abstract
new DIANA_2020.Abstract_Base(Class => Class) with record
-- Quick handle to this element within the tree; because the cursors are linked
-- to the single tree, all refrences into the tree should be via cursor, not access.
This : Tree_Package.Cursor := Tree_Package.No_Cursor;
Location : Source_Position:= (Attribute_Package.BASE with null record);
-- All other common components.
end record;
------------------------
-- Classwide of BASE --
------------------------
-- Uses a Tag to call the appropriate tagged-type's constructor.
Function Parameterized_Create(
Tag : Ada.Tags.Tag;
Params : Stream_Parameters
) return BASE'Class;
-- We'll need fix-ups, and such for referencing; see below.
-----------------------------------------
-- Interfacing/Inherited Subprograms --
-----------------------------------------
-- Provides creating an object of the proper type given the
Function Create(Params : not null access Stream_Parameters'Class)
return BASE is abstract;
Type EXP_CLASS(Class : Diana_2020.Classification) is abstract
new DIANA_2020.Base(Class => Class) with null record;
Type Node(Class : Diana_2020.Classification) is
new DIANA_2020.EXP_CLASS(Class => Class) with null record;
Type Tree(Class : Diana_2020.Classification) is
new DIANA_2020.EXP_CLASS(Class => Class) with record
Op : OPERATOR_CLASS'Class;
Left, Right : EXP_Class'Class;
end record;
-- Actually, since those (Op, Left, and Right) are all descendants of BASE_CLASS, they
-- should actually be children of Tree's THIS cursor within DIANA's ADT-Tree (the
-- instance of `DIANA_2020.Tree_Package.Tree`) you're working with, but that complicates it.
Private
-- Instantiate the dispatching-constructor…
Function Constructor is new Ada.Tags.Generic_Dispatching_Constructor(
T => Base,
Parameters => Stream_Parameters,
Constructor => Create
);
-- …then rename it for public view.
Function Parameterized_Create(
Tag : Ada.Tags.Tag;
Params : Stream_Parameters
) return BASE'Class renames Constructor;
End Diana_2020.Nodes;
-- So, We need to pick up the cursor, then set the THIS field to that, as it was initialized
-- with This as NO_ELEMENT; we need to ensure that the children are fixed also.
Procedure Fix_Up(
Element : in out BASE'Class;
Object : in out Tree_Package.Tree;
Location : in Tree_Package.Cursor;
) is
-- Here we pick up the cursor…
Procedure Fix_This( Cursor : Tree_Package.Cursor := Location ) is
-- …and here we correct the bad inital-value.
Procedure Fix_up(Element : in out BASE'Class ) is
begin
Element.This:= Cursor;
end Fix_Up;
Begin
if Tree_Package.Has_Element( Cursor ) then
Object.Update_Element( Position => Cursor, Process => Fix_Up'Access );
end if;
End Fix_This;
Begin
-- Fix this element.
Element.This:= Location;
-- Fix the children.
Object.Iterate_Subtree( Process => Fix_This'Access );
End Fix_Up;
Procedure Fix_Up(Object : in out Tree_Package.Tree) is
Procedure Fix_This( Cursor : Tree_Package.Cursor ) is
Procedure Fix_up(Element : in out BASE'Class ) is
begin
Element.This:= Cursor;
end Fix_Up;
Begin
if Tree_Package.Has_Element( Cursor ) then
Object.Update_Element( Position => Cursor, Process => Fix_Up'Access );
end if;
End Fix_This;
Begin
-- Fix Everything.
Object.Iterate( Process => Fix_This'Access );
End Fix_Up;
And so you don’t need to be quite as complex as you’re thinking.
Ah, I didn’t have enough time to make it shorter/simpler. And there was a bit of mismatch on the design and the example I chose — the dangers of not having a solid design.
But let’s go over what I didn’t mention/explain:
The OOP “programming by extension” means that type-wise/conceptually, what you’re doing in the type-system is continual refinement of specificity by continual expansion of “form and function”
Example: given PLANET, there are some that are LIFEBEARING and thus have the field “Population”; given LIFEBEARING there are some that are SENTIENT_LIFEBEARING and thus have a “Technological_Level” field.
Given the above, you could directly correspond DIANA nodes to an Ada tagged-type, and attributes to fields.
This allows you to build subprograms operating on entire classes of things —Ada’s classwide operations— such as, perhaps, Death_Star( Object : PLANET'CLASS ). (This corresponds to operations on DIANA’s class, and thus/also enables generalized processing, i.e. that of DIANA being a tree and doing tree/subtree operations.)
Introducing a non-classwide operation on some ancestor-type means that that and all derrived-types will implement that operation.
There are abstract tagged-types, of which no objects exist; these correspond to DIANA classes; you can make non-classwide subprograms, as above and be assured that all descendents will have it as an operation; thus giving you an interface.
Given the above, the elements of a language (natural or constructed) are much more easily handled —
Example: instead of having to throw a set of Nouns*Verbs operations, you need only have to do one set of Noun-class*Verbs (i.e. Verbs);
Some of the above can be done with Ada83’s generic, namely the polymorphic aspect
When implementing in a generic, you can only use the properties of the formal parameters, regardless of the actual that you’re instantiating with.
Along with the ability to pass subprograms, this allows you to construct [sometimes awkwardly] the an Abstract Data Type.
Given your complaint on DIANA’s complexity, and your comment regarding documentation, I’m going to guess what you’re trying to do is reverse-engineer DIANA/IDL absent the specification, absent the conceptual-design. (It is a LOT more work, and requires more ‘brainpower’/thinking to do this than most realize.) — If I’m at all correct there, you really ought to pause, read the DIANA reference-manual.
Just a side note, I kinda feel like this is a big gray area to me. I think you can definitely go overboard with inheritance, but I’ve generally not had any trouble in designs that were only 1-2 levels deep in inheritance, like designs that use interfaces to simplify their business logic and the only inheritance is implementing the interfaces (doing top down design). I’ve also seen the bad where you have like 15 levels of inheritance and you are busily trying to track down which ancestor implements what or where an operation originated from. I’ve also seen decently deep inheritance trees that were not bad at all, all of the base operations were done at a specific level and children only implemented, never added, so the API was easier to follow.
I’ve also seen projects that would have benefited from using inheritance with pages upon pages of case statement hell. Terrible maintenance hazards, hard to understand and follow logic.
I don’t think (at least for me) it is so cut and dry whether inheritance simplifies or not. I think it depends on what it is and how the inheritance is implemented. I feel like the pendulum can swing both ways.
Sorry for the long rambling. I’m not really trying to disagree or agree at all with the overall topic. I was just thinking about what you said and those were my thoughts.
I think it’s is highly dependent upon the problem-space.
And addressing things in the problem space is exactly where Ada’s type-system shines — granted there are some warts, and misdesigns (both of which should/could be corrected) — but overall, I’m of the opinion that Ada is the best language out there for “using the type-system to describe the problem-space, then using that to solve the problem.”
This seems to crop up when you run into using the wrong tool for the job: trying to shove something procedural into the functional world (e.g. some Java messes), and trying to shove something OOP-hierarchal (in nature) into something procedural (e.g. C code trying to do OOP).
Given the complaint and [indirect] descriptions @VMo has put forward, I think that the latter is what’s tripping him up: trying to wrap his head around the procedural-implementation of something that is (at the core) emulating that class-structure. (It’s always harder trying to intuit/discover a complex-system that has been realized using primitive or inappropriate formulation, and quintuply so for both. This is why full-featured APIs in C are so terrible: even if your idea is “this remote-file retrieval procedure succeeds or else errors with NOT_FOUND, INCOMPLETE_TRANSFER, or ACCESS_DENIED” devolves into a function returning an int: C has no exceptions, and enumerations are merely labels for integer-values.)
(NOTE: By “primitive formulation” above, I mean violation of the design-principle: everything should be as simple as possible, but no simpler.)
but in the IDL file procedure_id has no proper attribute :
procedure_id =>
All its attributes are inherited from one of the 7 levels.
Obviously, to write the code_gen module, it is the full developed form which is useful.
But this useful form is nowhere present in the IDL description. It must be generated for the user.
The procedure IDL.PRETTY_DIANA does it from $$$.TMP the work file to produce $$$_TREE.TXT in ./bin directory. DIANA_NODES.odt or DIANA_NODES.pdf file also show this developed form).
There are 231 nodes like procedure_id, 99 classes like ALL_SOURCE and 173 attributes like lx_srcpos …
Your reflexions are “frappées au coin du bon sens” as we say here. Translation, I don’t know, DeepL says “common sense at its heart”, which seems to me not exactly what the french expression contains.
I have an issue right now with static predicates and ambiguous expression which isn’t ambiguous at all. I’m starting to think I should stick to Ada 83 features as much as possible.
Your post implies your overall experience with many Ada95+ features hasn’t gone well. Given that Ada95 should be pretty stable at this point, I would be surprised if that really has been the case for you.
This.
The classification of language-entities is typically not very deep, especially WRT those classifications that are meaningful to later-processes.
Can you post the example?
(Something to note, the parameters of a generic cannot be considered static, as that would impose/imply a particular implementation [macro-like expansion vs shared].])
Honestly, the big problem is complexity.
Combinatoric-explosion is a thing, and oddly it seems that GANT in particular is not particularly good in this respect —this is one reason why I want to have a good meta-language: so that we can define the language in the [meta]language, using SPARK-like proofs and model-checking to ensure correctness and consistency— and one area where an IR that is DIANA-like (or, TBH, AIR-like) would be useful: by collecting all the defined-to-be-equivalent forms (e.g. return, extended-return, etc) and equivalent concepts (case and if/elsif/else as “conditionals”) into unified concept-nodes you simplify what you have to deal with.
(There was “Much Rejoicing!” with the addition of Ada 2022’s “dot-notation for all types”— I voted against that: I would rather have removed dot-notation altogether, for a more Ada95 experience, but was outvoted. — The reason is because the various syntactic forms yield more complexity [combinatorial-explosion in particular] of semantically-equivalent notation.)
I am convinced that for many software projects Ada 83 would be sufficient. Its complexity is already great but manageable. The object programming features add more complexity and the benefit seems to me doubtful in many circumstances. The compiler and language engineers must work, transform, augment ; but the basic user needs stability and well proven useful concepts, and tools of adequate complexity.
The day I read Smalltalk manual explaining the “everything is object” view of 2 + 5, that is : “Mister object Two, would you mind + yourself to argument five”, and coming from the Fortran numerical computing world, you think “Yes, we’re gonna see funny things in programming”.
Now getting back to the topic “Discussions about open source Ada 83 compilers”, it appears that the Ada 83/Diana translator I work with is the only full Ada 83 open source front end that translates itself. With an Ada 83 written code_generator it would be the only Ada 83 open source compiler which compiles itself.
I do not know what was your problem, but passing subprogram as a parameter is possible in Ada 83. When possible, a generic parameter can be used.
Otherwise it is discouraged, but when border line manipulation is required in system programming you have subprog’ADDRESS attribute to get an entry point LRM 13.7.2(3). Then call elsewhere with an address clause for a corresponding suprogram specification (for subprog use at …) LRM 13.5(2).
I used this trick extensively for jump tables (tables of contents TOC) in Kalinda OS.
As stated, to pass a subprogram to another subprogram. Access to a subprogram was first introduced in Ada 95. [Generics do not qualify, being a compile time stuff.]
Possibly but I don’t have the time to find out. Ada is generally fantastic and it was likely a knee jerk reaction. The times that features are problematic probably outshine when they are helpful which leaves you building a feeling of wanting a simpler super reliable language whether a valid point of view or not. I shall just maybe avoid using predicates with procedure overrides
Contrary to “case statement hell” I have added more case statements to replace the overrides that may appear redundant but mean the compiler will highlight any changes to which timers are in which predicates. So I am still using an Ada 2012 feature and replaced the Ada 83 feature (override) combined with a 2012 (predicate) with an Ada 83 case statement on the 2012 predicate.
Update: That caused a regression with null values causing pitential changed being missed so I have switched to a simple enum and case statement. A bit more writing in the enum vs predicates but maybe less lines of procedures and cases.
Ok, but that’s a short statement. subprog with parameter profile ? What context ? Ada 83 drastically limits access operations, but allows some workaround in some limit situations.
True, Ada 95 introduced all sorts of “access to” things. If you really can’t do without, Ada 95 or later is the choice. I have no problem with that, the tool must do the job.
It becomes off-topic, but you need no access type to pass a parameter. Ada 95 chose a shortcut in order to avoid the problem of subprogram types and subprograms as objects. As always, it hit back in the end.
I would say 2005, multiple inheritance is an important tool even half-baked form of interfaces.