Recent

Author Topic: Compiler can't find a (fairly clearly defined) constant in a USEd unit  (Read 531 times)

tfurnivall

  • New Member
  • *
  • Posts: 49
(See attached project - CATCOMP)

I have moved on a bit to create at least stubs for some of my regularly used code. Of particular interest in this post are the units symutil and lexutil in the CATCOMP project.

Symutil starts off:
Code: Pascal  [Select][+][-]
  1. unit symutil;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. { ***************************************************************
  8.   ***                                                         ***
  9.   ***    SYMUTIL - Configuration unit for LexUtil             ***
  10.   ***                                                         ***
  11.   ***    This unit is a model for the symutil unit that is    ***
  12.   ***    required by LexUtil. All the types and constants     ***
  13.   ***    in this model must be either copied or modified to   ***
  14.   ***    provide configuration data for LexUtil.              ***
  15.   ***                                                         ***
  16.   ***************************************************************
  17. }
  18. uses
  19.     Classes,
  20.     SysUtils,
  21.     SDLFS01;
  22.  
  23. const
  24.      ALFALEN            = 16;    { The length of an identifier in LexUtil }
  25.      NUMDIRECTIVES      = 32;    { The number of directives for LexUtil   }
  26.      NUMRESERVEDWORDS   = 32;    { Number of ReservedWords in your project}
  27.  

and Lexutil includes the following:

Code: Pascal  [Select][+][-]
  1. unit LexUtil;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. { ***************************************************************
  8.   ***                                                         ***
  9.   ***    LEXUTIL - Generic scanner and symbol extractor       ***
  10.   ***                                                         ***
  11.   ***    When USEing LexUtil you must also prepare a symutil  ***
  12.   ***    unit - in your projects directory, so that the       ***
  13.   ***    definitions for YOUR project can be incorporated     ***
  14.   ***    into LexUtil. Take a look at the symutil in          ***
  15.   ***    the components directory for a barebones skeleton    ***
  16.   ***    unit. This will compile - but will not do anything!  ***
  17.   ***                                                         ***
  18.   ***************************************************************
  19. }
  20. uses
  21.  Classes,
  22.  SysUtils,
  23.  symutil,           { Configure this unit for your project }
  24.  sdlfs01;
  25.  
  26. const
  27.  
  28. LEXUTILVERSION      = '*.00.000:000';
  29. LEXUTILVERSIONDATE  = '2025-06-07';
  30.  
  31. type
  32.  
  33. alfa=packed array[1..alfalen] of char;
  34.  
  35.  

My expectation is that because:
1) LexUtil uses symutil before it tries to create any code of its own, and
2) ALFALEN and alfalen are effectively synonymous,

then using alfalen in lexutil should reference the constant declared in symutil.

But it doesn't!
Indeed adding a new declaration of ALFALEN within Lexutil does not create an error, which I would have expected it to!

I'm also convinced that it's my problem - I'm just not understanding scoping rules. Does not the interface part of a unit become part of the global scope of any unit that uses it?

Tony

paule32

  • Hero Member
  • *****
  • Posts: 563
  • One in all. But, not all in one.
try to use the second UNIT with a USES in the IMPLEMENTATION Part in the first used UNIT.

Else, you will end up in/with circular circumstances (loop of call of caller).
MS-IIS - Internet Information Server, Apache, PHP/HTML/CSS, MinGW-32/64 MSys2 GNU C/C++ 13 (-stdc++20), FPC 3.2.2
A Friend in need, is a Friend indeed.

tfurnivall

  • New Member
  • *
  • Posts: 49
Thanks for the response, paule32,

If I understand you you suggest that, in the implementation section of SymUtil, I include
Code: Pascal  [Select][+][-]
  1. uses LexUtil;
  2.  

But this would create exactly the loop that you suggest I am trying to avoid. Wouldn't it?

<Pause while I just try it>

I set up a Lab environment (TestScope - attached). It's pretty minimal, so I've got it here:
First - a unit that defines  a couple of identifiers:
Code: Pascal  [Select][+][-]
  1. unit DefiningUnit;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.     Classes, SysUtils;
  9.  
  10. const
  11.   alfalen      = 16;
  12.  
  13. type
  14.   DefiningEnum = (
  15.                   value0,
  16.                   value1);
  17.  
  18. implementation
  19.  
  20. end.
  21.  
then a unit that consumes (or uses) those identifiers:
Code: Pascal  [Select][+][-]
  1. unit ConsumingUnit;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.     Classes, SysUtils,
  9.     DefiningUnit;
  10.  
  11. var
  12.   CUVar1   : DefiningEnum;
  13.   CUVar2   : array [1..alfalen] of integer;
  14.  
  15. implementation
  16.  
  17. end.
  18.  

mirabile dictu this works (without the additional 'reverse' uses clause).
So, why does it not work when there's a load of extra stuff in each of the units (symutil & lexutil)?
Just for kicks, I googled the following question:
Code: Pascal  [Select][+][-]
  1. what parts of the interface section of a Lazarus unit are available to code that uses the unit?

and received the following response from their AI bot:
Code: Pascal  [Select][+][-]
  1. In a Lazarus unit, the interface section defines what parts of the unit are publicly available to other units or programs that uses it. This includes declarations of types, constants, variables, procedures, functions, classes, and interfaces that are intended for external use.
  2. Elaboration:
  3.  
  4.     uses clause:
  5.     The uses clause at the beginning of the interface section specifies other units that are needed by the current unit. This helps the compiler resolve references to types, variables, and other declarations defined in those other units.
  6.  
  7. Types, constants, variables, and interfaces:
  8. Declarations of these elements made in the interface section become accessible to other units that use the current unit.
  9. Procedures and functions:
  10. Declarations of procedures and functions in the interface section become available to other units for calling and using their functionality.
  11. Classes:
  12. The declarations of classes in the interface section, including their public members (methods and properties), become accessible to other units.
  13. Initialization and finalization blocks:
  14. While these are not directly part of the interface, their presence is typically indicated in the interface section and they can contain code that runs when the unit is loaded or unloaded.
  15. Implementation details are hidden:
  16. The implementation section of the unit contains the actual code for procedures, functions, and methods declared in the interface. This code is not exposed to external units and is only visible to the current unit.
  17.  
  18. In essence, the interface section acts as a contract, defining the publicly available API of the unit. Other units that include it in their uses clause can access and use the declarations in the interface section, but they cannot access the implementation details.
  19.  

The AI matches my expectation of what should happen. So, what am I doing wrong in the original (CATCOMP) post?

Tony


paule32

  • Hero Member
  • *****
  • Posts: 563
  • One in all. But, not all in one.
if you would like to use the Consuming Members in Defining, you shall try to include the Consuming unit per USES in the implementing Part of Defining Unit:

unit DefiningUnit;
...
interface
...
implementation

uses
  ConsumingUnit;

const...
type...
var...
procedure ...
function ...
...
end.

analog to the Consuming unit:

unit Consuming;
...
interface
...
implementation

uses
  DefiningUnit;

const...
type...
var...
procedure...
function...
...
end.

Take care, that you don't use both Unit's in implementation Part of each together. One Unit should be view as Master, and the other Unit as Slave.

So, the Master holds the used global Members.
And the Slave holds the "usage" of the Members.

Can be a little bit tricky, I know ... But: Programming is Work, not Fun - sometimes  ;D
MS-IIS - Internet Information Server, Apache, PHP/HTML/CSS, MinGW-32/64 MSys2 GNU C/C++ 13 (-stdc++20), FPC 3.2.2
A Friend in need, is a Friend indeed.

tfurnivall

  • New Member
  • *
  • Posts: 49
Ah!

But I don't want to do that!

DefiningUnit doesn't want to have anything to do with anything from ConsumingUnit.
ConsumingUnit is (if you will pardon the lousy humor) consumed with a need to know everything that DefiningUnit wants to tell it. (But not how it does it).

ergo:

DefiningUnit exposes alfalen and DefiningEnum.
Consuming unit uses them.

Why do I need to refer to ConsumingUnit in DefiningUnit? If I take that to it's somewhat illogical extereme, I could never use another unit (say AnonymousUnit) without adding my code as a used unit inside AnonymousUnit.

As I said in my earlier post, these two units actually work (well, they compile, but that's where I'm having problems for now) properly. But the first post (CATCOMP) does not. A constant clearly defined in symutil is referenced when lexutil uses symutil. This means that alfalen should be available to lexutil.

But it isn't!

And I can't figure out why not - even though AI agrees with me that that's what ought to happen!

Perplexed,

Tony

cdbc

  • Hero Member
  • *****
  • Posts: 2249
    • http://www.cdbc.dk
Hi
I've just compiled CATCOMP without __any__ problems at all!
Q: why do you want 2 separate _and_ seems different sets of "symutil/lexutil"?!? -- Don't you (or the compiler) get confused?!?
What exactly is your problem?
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

tfurnivall

  • New Member
  • *
  • Posts: 49
Well that's interesting!

I cut back all sorts of bits and bobs in the code in order to get to the uploadable project.

So I went to a different location and downloaded the zip file that I uploaded - and I got exactly the same result.

So what I'll do is prune what I have in my 'regular' (erroneous') setup, and see when the compiler begins to accept it.

Now to find a place where I can save the starting (errorneous) version...

Thanks, Benny!

tfurnivall

  • New Member
  • *
  • Posts: 49
There is a very faint glimmer of light at the end of this tunnel. (I don't know that I can express it clearly - not least because I can't really see it clearly).

I started off by commenting out all uses of both symutil and lexutil and then removing those units from the project.
The project compiled.

Then I added back symutil - from the _Components directory of the CATCOMP project. (Directory structure is:
Code: Pascal  [Select][+][-]
  1. {starting at LAZARUS Projects}
  2. \LAZARUS Projects\
  3.     _Components
  4.     _LABS\
  5.     _CATCOMP\
  6.           _Components\
  7.                 backup\
  8.                 lexutil.pas
  9.                 symutil.pas
  10.           backup\
  11.           Documentation
  12.           lib\
  13.           PublishedForExport\
  14.           CATCOMP.exe
  15.           CATCOMP.ico
  16.           CATCOMP.lpi
  17.           CATCOMP.lpr
  18.           CATCOMP.lps
  19.           CATCOMP.res
  20.           catutil.pas
  21.           unit1.lfm
  22.           unit1.pas
  23.     TOOLS\
  24.     COMPLST.txt
  25.     COMPOUT.txt
  26.     COMPSYM.txt
  27.  

No credit for those who have figured it out, already!

My ProjectInspector file contained only
Code: Pascal  [Select][+][-]
  1.  Files
  2.      CATCOMP.lpr
  3.      unit1.pas
  4.      catutil.pas
  5.      ..Components\symutil.pas
  6.  Required Packages
  7.      LCL
  8.  

And that's when I noticed that the reference to lexutil (not yet added back - but available in my own copy of the downloaded upload that I posted to the forum) had a very slightlydifferent path value:
Code: Pascal  [Select][+][-]
  1.    ..\_Components\lexutil.pas
  2.  

So that explains why dropping and adding the value of lexutil in CATCOMP\_Components had no effect!
Also, why, when I added in a symutil (from CATCOMP\Components it promptly went and sought out the lexutil from
Code: Pascal  [Select][+][-]
  1. LAZARUS Projects\Components\lexutil.pas
  2.  

Newby error, but not helped by the lack of documentation.

I now have compiler errors but at least I know whence they arise.

I won't mark this [SOLVED] until I get the demo version of CATCOMP working, and properly boxed up for those who have the patience of Job to play with - if they so desire.

Thanks to those who nudged me in the right direction! Now to see if I can stumble a little further under my own steam!

Tony

cdbc

  • Hero Member
  • *****
  • Posts: 2249
    • http://www.cdbc.dk
Hi Tony
No worries mate -- Happy coding  ;)
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

 

TinyPortal © 2005-2018