I have an idea but I am not sure of the requirement.
Background
The other day there was a post where a program had been created that had a missing prerequisite command. The example was for hardware I2C. Where you cannot use HWI2CSend ( as this example ) without having HWI2CMode MASTER | SLAVE. So, if HWI2CSend is used and HWI2CMode is missing the code is compile but the program does not work.
Where the compiler could check the user program for the use of HI2CSend and the syntax prerequisite is HI2CMode. If HI2CMode is not present in the user code then issue the error "Missing command HI2CMode".
Same for serial. Where the compiler could check the user program for the use of HSerPrint and the syntax prerequisite is a constant called USARTBAUDRATE. If USARTBAUDRATE is not present in the user code then issue the error "Missing Constant USARTBAUDRATE".
Does this add value ? What else could this cover?
Can you think of other uses?
Evan
*
Last edit: Anobium 2021-06-23
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Both your examples make good sense, it would be enlightening to have some indication of a potential eror.
If I could add another possible case to consider, that of "printing" to an LCD scren. On some projects I've made the LCD optional by surrounding it with conditional define statements. Should I accidentally leave any commands which are contained in the LCD header (and library) an error is raised but it makes an obscure reference to LCDSYSTEMP.0 and doesn't show the source of any error - a remaining Locate or print command usually in my case. Not sure if this could be easily fixed, and now I'm aware of it, I can track it down mostly, but it was very confusing and difficult to find the first few times when I've copied and passted "working" code from one program to another.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
We will need a single item to check. Like a single constant we can check against. What would you advise? Remember, we there are many different LCD configs.
We could have more than one prereq.
Evan
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
A laudable idea but a potential can of worms. @mkstevo has just highlighted one of the issues I was thinking of as a potential trap.
Print and Locate are two of the most commonly used BASIC methods but GCBASIC has them dedicated to LCD.
As a result it is common to use #Define to redirect i.e.
#define Print HSerPrint
This is especially useful when talking to a VT100 Terminal emulation which responds to commands such as Locate.
Those are the fist two command that come to mind but I am sure there are many others that have been used outside of the original GCBASIC intended context.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Make 'HWI2CMode MASTER' the default. At the moment there is no default hence the issue.
Add HWI2C port direction to the DAT files. So, we can make the setting of the ports automatic.
For HSerPrint operations. Get the compiler to examine the generated ASM. If the sub INITUSART has no code.. then, the HWUSART is not setup ie there is no USART_BAUD_RATE
For LCD. Get the compiler to examine the error generated by not having the LCD setup correctly. There are specific error messages that could be issued when the LCD ports are not defined. So, rather than (as-is) lcd.h (559): Error: SYSLCDTEMP.0 is not a valid I/O pin or port change to something like (could-be) lcd.h (559): Error: LCD_RW is not a valid I/O pin or port please ensure you have defined LCD_RW port constant in your program (this is one example).
This approach is a lot more work than a generic handler but we need to resolve this traps for users.
What are the other traps?
Last edit: Anobium 2021-06-23
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
It isn't a big problem for me, now I understand the cause(s) of it. If I could wave a magic wand, the error message raised would not point to "lcd.h", but to the position in my code that caused "lcd.h" to be built into my code when I wasn't expecting it. Or an optional Delphi like "Uses" clause. Where only units (or headers) declared as being "Used" were built and assembled?
As I say, it isn't a big problem, though it has confused me from time to time.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Would enabling or allowing an optional "Uses" clause {similar in my mind to "Option Explicit"} to prevent automatic inclusion of system libraries and headers be more than a few lines of code?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The GCB libraries are only included if the user program calls a sub, function or macro (the methods) within that specific library file.
Example LCD.h it is automatically '#included' however the methods are not compiled unless the user calls one of the methods. Like PRINT. So, then, the #startup is called, which in turn will call the INITLCD.
Example HEF.F it is automatically '#included' however the methods are not compiled unless the user calls one of the methods. As very few programs call the HEF methods no code is compiled.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks for giving some clarity on how that works in some greater detail.
The way my mind works, I would prefer to see no methods compiled (or made available) at all, unless I requested them.
When a new unit was created in Delphi, it automatically added some of the 'standard' libraries to a default "Uses" clause - a little like this:
usesWindows,Messages,SysUtils;
Most programs would require the 'Windows' unit, but in very rare TSR non-GUI cases this could possibly be removed and then not compiled. Likewise 'Messages' and SysUtils. If a procedure then attempted to utilise something that was within 'SysUtils' , an error would be raised - hopefully at, or near to, the section of code which tried to call the missing library.
I concede that it is almost certainly just me that would benefit from a methodology which put the onus on the programmer to decide which libraries would potentially be included (or excluded), but it would be on my wish list.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Compile this and you will see in the ASM as follows. Note there is NO real program ASM, no config, no INITSYS. Just the ORG statement and the PAGESEL. This is the highest level of optimisation. There is essentially no real code.
You will see that INITSYS is called. INITSYS is part of system.h so the compiler 'uses' system.h automatically. How?
Find this file C:\GCB@Syn\GreatCowBasic\Include\lowlevel.dat. Take a copy of all the entries in this file. Now remove all the entries. Save. Compile the program.
#chip 16f877a
#option Noconfig
You will see INITSYS is missing. Because the compiler cannot find a method called INITSYS - so, it does not add.
So, what is lowlevel.dat? It is essentially a set of multiline #include statements.
Compile, review the ASM and you will see the INITSYS is now present.
Now revert lowlevel.dat and save.
So, lowlevel.dat contains all the default includes. These entries MUST NOT and CANNOT be removed as there are dependencies between these include files and I truly dont know what these dependencies are - you will simply break the compiler.
Think of lowlevel.dat being the equivalent of uses i.e.
Remember, removal of any of the entries in lowlevel.dat WILL NOT reduce the code generated. Code is only generated from any of these lowlevel include files when a method is called (for every library 'used' the #startup [if present] is called to initialise the library).
And, if any of these libraries add program code when not 'used' then this would be a bug and we would resolve.
Hugh has done a wonderful job on the optimisation and the simplicity of solution with respect to these include files. The compiler does a many things to make the programming experience simple.
Lowlevel library support is an example of ease of use.
Last edit: Anobium 2021-06-24
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
It isn't that I don't appreciate the optimisation the the compiler does, and how well it is able to determine which libraries or parts of libraries are compiled.
I have no issue with the size of the compiled programs nor how minimal the footprint of compiled code is, the compiler is the work of many geniuses, far cleverer than me and much better programmers too.
It was just a thought that ran through my head.
It was a feature of Delphi that I used extensively as I modified and extended the original libraries.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I can, but with the proviso that I only update an existing library, I've not found a way to have both. The altered library can't seem to be renamed as 'My_extendedLibrary.h' and used alongside the original, switching between the two with a 'Uses' clause, as the original library appears to always be called if any of the functions and subroutines have the same names - which they would.
It is at this point I felt a 'Uses' clause would be benefficial. I could see at a glance what libraries I was, and wasn't using - modified or not.
As I' said, it was a thought that popped into my head. Something I liked and used a lot.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Given that it would break the compiler and it isn't anything that is required by anyone else, I think the thought should be popped back into my head, shoved to the back and not let out again. Ever!
Last edit: mkstevo 2021-06-25
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Oh, I can sympathize with the dilemma on alt libraries. Valid reasoning all around. Ultimately I have had to rename the methods within my alt libraries so as to coexist with existing libraries, kind of a hassle, but thankfully not used much.
It is an Easter egg hunt when I need to use one when last used 5 compiler revisions ago.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
You can replace any method with your own method. Then, you can upgrade the compiler with some ease. Do this in your user code leaving the original library method intact.
#define UsartInit myUsartInit
where the first parameter is the original method, and, the second parameter the new target method.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I have an idea but I am not sure of the requirement.
Background
The other day there was a post where a program had been created that had a missing prerequisite command. The example was for hardware I2C. Where you cannot use HWI2CSend ( as this example ) without having HWI2CMode MASTER | SLAVE. So, if HWI2CSend is used and HWI2CMode is missing the code is compile but the program does not work.
Potential Solution
See https://sourceforge.net/p/gcbasic/discussion/579125/thread/ce0f6cfd8e/#7f2d the conversation has moved on.
*This is a potential solution of this example and others examples (however, I can only think of two examples at the moment).
We add syntax checking for prerequisite commands. Like this.
Where the compiler could check the user program for the use of HI2CSend and the syntax prerequisite is HI2CMode. If HI2CMode is not present in the user code then issue the error "Missing command HI2CMode".
Same for serial. Where the compiler could check the user program for the use of HSerPrint and the syntax prerequisite is a constant called USARTBAUDRATE. If USARTBAUDRATE is not present in the user code then issue the error "Missing Constant USARTBAUDRATE".
Does this add value ? What else could this cover?
Can you think of other uses?
Evan
*
Last edit: Anobium 2021-06-23
Both your examples make good sense, it would be enlightening to have some indication of a potential eror.
If I could add another possible case to consider, that of "printing" to an LCD scren. On some projects I've made the LCD optional by surrounding it with conditional define statements. Should I accidentally leave any commands which are contained in the LCD header (and library) an error is raised but it makes an obscure reference to LCDSYSTEMP.0 and doesn't show the source of any error - a remaining Locate or print command usually in my case. Not sure if this could be easily fixed, and now I'm aware of it, I can track it down mostly, but it was very confusing and difficult to find the first few times when I've copied and passted "working" code from one program to another.
@mkstevo
LCD should be resolvable.
We will need a single item to check. Like a single constant we can check against. What would you advise? Remember, we there are many different LCD configs.
We could have more than one prereq.
Evan
This approach may have to an extension to the current source. Something like
PSUEDO CODE - expand this
The concept code.
Edited as we cross posted.
Locate and (or) Print are the two that I find trip me up.
Would that make sense?
Last edit: mkstevo 2021-06-22
A laudable idea but a potential can of worms.
@mkstevo has just highlighted one of the issues I was thinking of as a potential trap.
Print and Locate are two of the most commonly used BASIC methods but GCBASIC has them dedicated to LCD.
As a result it is common to use #Define to redirect i.e.
This is especially useful when talking to a VT100 Terminal emulation which responds to commands such as Locate.
Those are the fist two command that come to mind but I am sure there are many others that have been used outside of the original GCBASIC intended context.
@caroper You are correct. Trying to trap HWSerial with this approach will fail. Too many traps like the one you highlighted.
May be HWSerial is in the too hard category.
So, thinking aloud.
An alternative approach.
lcd.h (559): Error: SYSLCDTEMP.0 is not a valid I/O pin or port
change to something like (could-be)lcd.h (559): Error: LCD_RW is not a valid I/O pin or port please ensure you have defined LCD_RW port constant in your program
(this is one example).This approach is a lot more work than a generic handler but we need to resolve this traps for users.
What are the other traps?
Last edit: Anobium 2021-06-23
It isn't a big problem for me, now I understand the cause(s) of it. If I could wave a magic wand, the error message raised would not point to "lcd.h", but to the position in my code that caused "lcd.h" to be built into my code when I wasn't expecting it. Or an optional Delphi like "Uses" clause. Where only units (or headers) declared as being "Used" were built and assembled?
As I say, it isn't a big problem, though it has confused me from time to time.
I may be able to point to the incorrect constant but today the libraries do not any concept of what was called to create the error.
So, I am thinking of adding a 'clue' to the library to aid the messaging when an error happens.
No promises this will happen yet. Just gathering requirements at the moment.
Thanks for your consideration.
Would enabling or allowing an optional "Uses" clause {similar in my mind to "Option Explicit"} to prevent automatic inclusion of system libraries and headers be more than a few lines of code?
How would this help?
The GCB libraries are only included if the user program calls a sub, function or macro (the methods) within that specific library file.
Example LCD.h it is automatically '#included' however the methods are not compiled unless the user calls one of the methods. Like PRINT. So, then, the #startup is called, which in turn will call the INITLCD.
Example HEF.F it is automatically '#included' however the methods are not compiled unless the user calls one of the methods. As very few programs call the HEF methods no code is compiled.
Thanks for giving some clarity on how that works in some greater detail.
The way my mind works, I would prefer to see no methods compiled (or made available) at all, unless I requested them.
When a new unit was created in Delphi, it automatically added some of the 'standard' libraries to a default "Uses" clause - a little like this:
Most programs would require the 'Windows' unit, but in very rare TSR non-GUI cases this could possibly be removed and then not compiled. Likewise 'Messages' and SysUtils. If a procedure then attempted to utilise something that was within 'SysUtils' , an error would be raised - hopefully at, or near to, the section of code which tried to call the missing library.
I concede that it is almost certainly just me that would benefit from a methodology which put the onus on the programmer to decide which libraries would potentially be included (or excluded), but it would be on my wish list.
Great Cow BASIC is smart. The only methods compiled are the methods used. It is highly optimised and therefore I see no benefit of the 'uses'.
To clarify. There are 100s of methods in the core libraries and very few are compiled.
Think that Great Cow BASIC optimises for you. Because, it does.
Last edit: Anobium 2021-06-23
As I said, it is something I'd quite like. I realise almost no-one else will.
@mkstevo
Am I missing the point?
I will provide more insights to try to show you that that Great Cow BASIC does what you want automatically.
Using the code below as the example.
Compile this and you will see in the ASM as follows. Note there is NO real program ASM, no config, no INITSYS. Just the ORG statement and the PAGESEL. This is the highest level of optimisation. There is essentially no real code.
Now, change to, compile.
You will see that INITSYS is called. INITSYS is part of
system.h
so the compiler 'uses' system.h automatically. How?Find this file
C:\GCB@Syn\GreatCowBasic\Include\lowlevel.dat
. Take a copy of all the entries in this file. Now remove all the entries. Save. Compile the program.You will see INITSYS is missing. Because the compiler cannot find a method called INITSYS - so, it does not add.
So, what is
lowlevel.dat
? It is essentially a set of multiline #include statements.Proof. Add the #include for system.h
Compile, review the ASM and you will see the INITSYS is now present.
Now revert lowlevel.dat and save.
So, lowlevel.dat contains all the default includes. These entries MUST NOT and CANNOT be removed as there are dependencies between these include files and I truly dont know what these dependencies are - you will simply break the compiler.
Think of lowlevel.dat being the equivalent of uses i.e.
uses picas.h, a-d.h, pwm.h, rs232.h, eeprom.h, sound.h, stdbasic.h, 7segment.h, lcd.h, ps2.h, timer.h, system.h, hwspi.h, keypad.h, random.h, string.h, usart.h, i2c.h, hwi2c.h, hwi2c2.h, pwm16.h, saf.h, hef.h, spisram.h
Remember, removal of any of the entries in lowlevel.dat WILL NOT reduce the code generated. Code is only generated from any of these lowlevel include files when a method is called (for every library 'used' the #startup [if present] is called to initialise the library).
And, if any of these libraries add program code when not 'used' then this would be a bug and we would resolve.
Hugh has done a wonderful job on the optimisation and the simplicity of solution with respect to these include files. The compiler does a many things to make the programming experience simple.
Lowlevel library support is an example of ease of use.
Last edit: Anobium 2021-06-24
It isn't that I don't appreciate the optimisation the the compiler does, and how well it is able to determine which libraries or parts of libraries are compiled.
I have no issue with the size of the compiled programs nor how minimal the footprint of compiled code is, the compiler is the work of many geniuses, far cleverer than me and much better programmers too.
It was just a thought that ran through my head.
It was a feature of Delphi that I used extensively as I modified and extended the original libraries.
Not a problem. You can adapt Great Cow BASIC also. ;-)
I can, but with the proviso that I only update an existing library, I've not found a way to have both. The altered library can't seem to be renamed as 'My_extendedLibrary.h' and used alongside the original, switching between the two with a 'Uses' clause, as the original library appears to always be called if any of the functions and subroutines have the same names - which they would.
It is at this point I felt a 'Uses' clause would be benefficial. I could see at a glance what libraries I was, and wasn't using - modified or not.
As I' said, it was a thought that popped into my head. Something I liked and used a lot.
Oh. That is what you want to do.
Can I clarify? You want to change/replace/update an existing method?
Given that it would break the compiler and it isn't anything that is required by anyone else, I think the thought should be popped back into my head, shoved to the back and not let out again. Ever!
Last edit: mkstevo 2021-06-25
Oh, I can sympathize with the dilemma on alt libraries. Valid reasoning all around. Ultimately I have had to rename the methods within my alt libraries so as to coexist with existing libraries, kind of a hassle, but thankfully not used much.
It is an Easter egg hunt when I need to use one when last used 5 compiler revisions ago.
You can replace any method with your own method. Then, you can upgrade the compiler with some ease. Do this in your user code leaving the original library method intact.
where the first parameter is the original method, and, the second parameter the new target method.
For sure, that is another way to handle it. Whatever it takes to keep things straight in our own individual minds :-)
This is the method I use to develop Great Cow BASIC. So, I can develop new capabilities.