I would not expect a single source to be able to target both P1 and P2 without heavy #ifdefs, or if it has no code that touches the hardware in any way.
Chip,
Right now, just get Spin2 up and running. We can worry about all this Spin2 on P1 business later. Let's just try to avoid diverging Spin2 too far away from Spin1. Like I have said a few times, I think Spin2 should be more of a superset of Spin1 (for the non-PASM stuff).
Chip, if you add #ifdefs to Spin2, can you consider using Pascal-style {$ifdef FLAG} {$else} {$endif} instead? I've always thought that the C-style #ifdef supported by Spin compilers other than yours didn't fit with the rest of Spin's syntax, while enclosing them in {} looks and feels much better. Not to mention the fact that Pascal-style {$ifdefs} can be embedded inside of lines.
Yeah, but it should also support Pascal style, since it allows conditional compilation of parts of lines as opposed to restricting you to whole lines. Also, I feel like Pascal style conditional compilation jibes better with the rest of Spin's syntax.
If you or someone writes the parsing for that, that will work without conflict in Spin code, then I'll include it. However, since { } are comments, you will probably have some issues, but maybe not? I don't care for it myself, but I am biased towards C style.
The existing C style preprocessor was provided to me. I didn't write it. So it's the same deal.
I'm not sure why ifdef and if for conditional compilation even need a special syntax. It should be clear during the first pass whether the conditionals evaluate to a constant or not. If they do, they should be treated as conditional compilation; if not, as a run-time conditional. IMO, the # prefix is not only ugly but completely unnecessary.
Phil,
you are always saying how it should be possible/easy to infer so many things just from context... but it's really not easy, and often not possible to do in all cases and situations. You have to consider every possible use case including things you wouldn't do.
I'm not sure why ifdef and if for conditional compilation even need a special syntax. It should be clear during the first pass whether the conditionals evaluate to a constant or not. If they do, they should be treated as conditional compilation; if not, as a run-time conditional. IMO, the # prefix is not only ugly but completely unnecessary.
Yes, certainly, compile-time constants, should behave just like conditionals, and remove code sections entirely.
However, there are still places where Board-variant or similar project controls are useful, and some editors support folding on the ifdefs, and even live high-lighting.
ie both is the best (as I always say, give designers the choice).
You have to consider every possible use case including things you wouldn't do.
Can you show me an example of a #-less if or ifdef that's ambiguous?
-Phil
The places where this sort of thing gets tricky is when you have separate compilation of modules and the value of a symbol isn't known until link time. This, of course, isn't possible in Spin since there is no linker. The entire program is compiled at once. Will this be true of Spin2 as well?
You have to consider every possible use case including things you wouldn't do.
Can you show me an example of a #-less if or ifdef that's ambiguous?
It could get awkward where you're trying to use features that are only available on certain chips, e.g:
if (PROP2)
waitct2(...)
else
waitcnt(...)
With an #ifdef you could always throw an error if e.g. a waitct2 is seen in Prop1 code. With constant if() the compiler can't do that, because it might be in a branch of an if that isn't actually going to be executed. Obviously with a fancy enough compiler there are ways to handle this, but it does complicate error handling a lot.
Phil,
The symbol names for the #ifdef stuff is not co-mingled with the other symbols, the spin compiler never even sees the source with any #ifdef/etc. in it. So you can have #ifdef <any synbol, even spin keywords or symbols matching ones in the spin code> and it will be fine. Your solution would require extra handling to account for that. But anyway, I think it's up to you to provide a preprocessor that can do what you want and prove it works in all the edge cases. Also, symbols can be defined mid file and don't apply globally. I didn't write the existing one, and I don't plan to write any new ones. If you want to be the one to take Chip's x86 asm and convert it to a open source high level language that works across platforms and is acceptable to Parallax, then feel free to step up. Then you can make it do whatever inferring you feel should be done. I'll enjoy seeing your solutions, because it's stuff that's not really done in other compilers, and there has to be reasons why. Also, you have until Fall (most likely) to get it completely done. See you then.
David,
Spin is actually kind of compiled and linked, it's just all done in one executable. The objects get compiled is a 2 pass approach, but ultimately leaf objects get code generated first and saved off, then when the parent is compiled the child objects symbols are injected and the binary blobs are stitched together, and this works it's way up to the top. So, in an A includes B includes C chain, C is compiled, then B is compiled with C linked in, then A is compiled with the combined B/C linked in. Then there are some post steps to distill duplicate objects, and finalize the memory into *.binary or *.eeprom form. For unused method/object elimination it does the whole shebang once, tracking a bunch of Smile, then then does it again with the tracked info to prevent compiling in unused stuff. I kind of want to change things so that all the objects get compiled and then a link pass puts them all together. It would simplify and clean up some things, but it also means changing the compile target format a bit to allow for linking. Then end result still has to be the exact binary form for the ROM interpreter to run it.
ersmith,
Spin already has to do non-traditional Smile just to deal with Spin/PASM being not really properly defined. Getting fancy on top of that just means a harder to debug and maintain mess in my opinion.
The places where this sort of thing gets tricky is when you have separate compilation of modules and the value of a symbol isn't known until link time. This, of course, isn't possible in Spin since there is no linker. The entire program is compiled at once. Will this be true of Spin2 as well?
I sure hope so. Linkers are a PITA and an unnecessary encumbrance on an elegant language like Spin. We simply don't need pre-compiled modules that need to be linked.
The symbol names for the #ifdef stuff is not co-mingled with the other symbols, the spin compiler never even sees the source with any #ifdef/etc. in it. So you can have #ifdef <any synbol, even spin keywords or symbols matching ones in the spin code> and it will be fine. Your solution would require extra handling to account for that. But anyway, I think it's up to you to provide a preprocessor ...
In C-world that's true. This isn't C, and I'm not convinced that if and ifdef can't be handled during the compile phase. You say the symbols used by #if and #ifdef are in a different dictionary, but they don't have to be. Defining them in the CON section, like any other constant, should suffice. No preprocessor needed -- at least for if and ifdef. Also, handling CONstants that get defined after they're used will depend upon how many passes the compiler takes through the source code.
Phil, the preprocessor happens first and outputs new source without the preprocessor stuff in it.
Also, if you are convinced, then go write it. OpenSpin is open source. Have at it.
Also, Spin is precompiled binary blobs that are linked together.... just all in one exe and the blobs are saved in a memory heap instead of on disk. See my reply to David.
Phil, the preprocessor happens first and outputs new source without the preprocessor stuff in it.
I know that. I'm just saying that the if and ifdef stuff doesn't have to be done by a separate preprocessor but could be included in a compilation pass.
Also, if you are convinced, then go write it. OpenSpin is open source. Have at it.
Too bad you didn't write it in Perl, else I'd take you up on it!
Phil,
But that does make it more work to parse/compile correctly. It's already hard enough, stop making it harder.
Also, I think you mispelled "Thankfully" as "Too bad". I can't believe you bad on C/C++ so much but love Perl (which is worse in just about every way that I care about). :P
But that does make it more work to parse/compile correctly. It's already hard enough, stop making it harder.
So it makes it a little harder. Why not, if you only have to do it once and if it makes the language more elegant. Eliminating as much syntactic gingerbread as possible is a worthy objective, IMO. Same applies to ? and ??, BTW.
This isn't C, and I'm not convinced that if and ifdef can't be handled during the compile phase.
They can, sure, and other languages/compilers already have that feature - however they also have the traditional define controls too.
No surprise, as there are good reasons for doing both.
The better Editors/IDEs also track the projects defines, and highlight accordingly.
As Roy says, code for all this already exists.
... however they also have the traditional define controls too.
But what value is there in a two-tiered approach, when the compiler itself can do constant folding and code elimination?
As Roy says, code for all this already exists.
Oh, I'm sure it does, replete with #endifs, rather than indentation scoping. Very unSpin-like!
BTW, standard Python appears not to have a preprocessor. Code elimination occurs during compile with standard if/else syntax, in the same way I'm proposing for Spin, viz:
if FLAG:
def f():
print "Flag is set"
else:
def f():
print "Flag is not set"
Why do we have separate compilation, object files and linkers?
I always thought it was a hack to get around the tight memory constraints of the computers that languages like C, Pascal etc were developed on. There just was not enough space to deal with a whole program in one go.
None of this mechanics is anything that a programmer wants when writing his code. It means you have to have header files and such redundancy.
I have often thought that a compiler could just effectively concatenate all the source files of a project and compile it in one go. We have memory enough now a days.
Sounds like Spin compilers go a long way in that direction.
Why do we have separate compilation, object files and linkers?
I always thought it was a hack to get around the tight memory constraints of the computers that languages like C, Pascal etc were developed on. There just was not enough space to deal with a whole program in one go.
None of this mechanics is anything that a programmer wants when writing his code. It means you have to have header files and such redundancy.
I have often thought that a compiler could just effectively concatenate all the source files of a project and compile it in one go. We have memory enough now a days.
Sounds like Spin compilers go a long way in that direction.
I think it's also for compile speed. If you have to parse hundreds of thousands of lines of code every time you compile something it takes a long time. Check the difference between typing "make clean; make" and just "make" after making a single line change in a really big C program. The difference is significant. Now, with P1 it has been said that with only 32K of hub memory you can't have very large programs. That is sort of true but if you start pulling in large objects most of whose methods are not used you may still end up parsing those methods even though you end up eliminating them as dead code later. With P2 having 512K of hub memory things could get even worse. I'm not saying that separate compilation should be added to Spin2. I'm just saying there are some advantages to it for other languages especially when you start writing large programs making use of large libraries.
As processors have become thousands of times faster over the years compilers have got slower to compensate. Compiling any non trivial C++ program takes forever.
As processors have become thousands of times faster over the years compilers have got slower to compensate. Compiling any non trivial C++ program takes forever.
I think Java solves this by writing out the compiled binary form of classes so it can avoid compiling them again if the source file hasn't changed. I did the same thing in my xbasic compiler for P1.
David Betz,
Yeah, I'm not using STL. That was the compile time with Visual Studio 2015 on my Windows 10 machine (pretty beefy Core i7 6700, 32GB ram, M.2 PCIe 4x NVMe SSD drives). I haven't tested since switching to 2017, but it seems similarly fast. The NVMe SSDs make a pretty significant difference, especially since most of the compile time is I/O.
Heater,
Qt5 is a pretty huge set of libraries, and I bet 90% of the time was spent on I/O for R. Pi.
That's right. I put all the sources and build files on an NFS share from my PC to speed things along a bit. Also a swap file as the old Pi does not have so much RAM. Things would go much better on a Pi 3.
David Betz,
Yeah, I'm not using STL. That was the compile time with Visual Studio 2015 on my Windows 10 machine (pretty beefy Core i7 6700, 32GB ram, M.2 PCIe 4x NVMe SSD drives). I haven't tested since switching to 2017, but it seems similarly fast. The NVMe SSDs make a pretty significant difference, especially since most of the compile time is I/O.
Heater,
Qt5 is a pretty huge set of libraries, and I bet 90% of the time was spent on I/O for R. Pi.
I just compiled OpenSpin on my Mac mini with a dual core 2.5 Ghz i5 processor and 16gb of RAM. It took a little under four seconds. Still pretty good and this is an old machine.
Comments
Chip,
Right now, just get Spin2 up and running. We can worry about all this Spin2 on P1 business later. Let's just try to avoid diverging Spin2 too far away from Spin1. Like I have said a few times, I think Spin2 should be more of a superset of Spin1 (for the non-PASM stuff).
OpenSpin already supports C style preprocessing, and will continue to, including for OpenSpin for P2.
The existing C style preprocessor was provided to me. I didn't write it. So it's the same deal.
-Phil
you are always saying how it should be possible/easy to infer so many things just from context... but it's really not easy, and often not possible to do in all cases and situations. You have to consider every possible use case including things you wouldn't do.
Yes, certainly, compile-time constants, should behave just like conditionals, and remove code sections entirely.
However, there are still places where Board-variant or similar project controls are useful, and some editors support folding on the ifdefs, and even live high-lighting.
ie both is the best (as I always say, give designers the choice).
-Phil
It could get awkward where you're trying to use features that are only available on certain chips, e.g: With an #ifdef you could always throw an error if e.g. a waitct2 is seen in Prop1 code. With constant if() the compiler can't do that, because it might be in a branch of an if that isn't actually going to be executed. Obviously with a fancy enough compiler there are ways to handle this, but it does complicate error handling a lot.
The symbol names for the #ifdef stuff is not co-mingled with the other symbols, the spin compiler never even sees the source with any #ifdef/etc. in it. So you can have #ifdef <any synbol, even spin keywords or symbols matching ones in the spin code> and it will be fine. Your solution would require extra handling to account for that. But anyway, I think it's up to you to provide a preprocessor that can do what you want and prove it works in all the edge cases. Also, symbols can be defined mid file and don't apply globally. I didn't write the existing one, and I don't plan to write any new ones. If you want to be the one to take Chip's x86 asm and convert it to a open source high level language that works across platforms and is acceptable to Parallax, then feel free to step up. Then you can make it do whatever inferring you feel should be done. I'll enjoy seeing your solutions, because it's stuff that's not really done in other compilers, and there has to be reasons why. Also, you have until Fall (most likely) to get it completely done. See you then.
David,
Spin is actually kind of compiled and linked, it's just all done in one executable. The objects get compiled is a 2 pass approach, but ultimately leaf objects get code generated first and saved off, then when the parent is compiled the child objects symbols are injected and the binary blobs are stitched together, and this works it's way up to the top. So, in an A includes B includes C chain, C is compiled, then B is compiled with C linked in, then A is compiled with the combined B/C linked in. Then there are some post steps to distill duplicate objects, and finalize the memory into *.binary or *.eeprom form. For unused method/object elimination it does the whole shebang once, tracking a bunch of Smile, then then does it again with the tracked info to prevent compiling in unused stuff. I kind of want to change things so that all the objects get compiled and then a link pass puts them all together. It would simplify and clean up some things, but it also means changing the compile target format a bit to allow for linking. Then end result still has to be the exact binary form for the ROM interpreter to run it.
ersmith,
Spin already has to do non-traditional Smile just to deal with Spin/PASM being not really properly defined. Getting fancy on top of that just means a harder to debug and maintain mess in my opinion.
In C-world that's true. This isn't C, and I'm not convinced that if and ifdef can't be handled during the compile phase. You say the symbols used by #if and #ifdef are in a different dictionary, but they don't have to be. Defining them in the CON section, like any other constant, should suffice. No preprocessor needed -- at least for if and ifdef. Also, handling CONstants that get defined after they're used will depend upon how many passes the compiler takes through the source code.
-Phil
Also, if you are convinced, then go write it. OpenSpin is open source. Have at it.
Also, Spin is precompiled binary blobs that are linked together.... just all in one exe and the blobs are saved in a memory heap instead of on disk. See my reply to David.
Too bad you didn't write it in Perl, else I'd take you up on it!
-Phil
But that does make it more work to parse/compile correctly. It's already hard enough, stop making it harder.
Also, I think you mispelled "Thankfully" as "Too bad". I can't believe you bad on C/C++ so much but love Perl (which is worse in just about every way that I care about). :P
-Phil
They can, sure, and other languages/compilers already have that feature - however they also have the traditional define controls too.
No surprise, as there are good reasons for doing both.
The better Editors/IDEs also track the projects defines, and highlight accordingly.
As Roy says, code for all this already exists.
Oh, I'm sure it does, replete with #endifs, rather than indentation scoping. Very unSpin-like!
BTW, standard Python appears not to have a preprocessor. Code elimination occurs during compile with standard if/else syntax, in the same way I'm proposing for Spin, viz:
-Phil
I always thought it was a hack to get around the tight memory constraints of the computers that languages like C, Pascal etc were developed on. There just was not enough space to deal with a whole program in one go.
None of this mechanics is anything that a programmer wants when writing his code. It means you have to have header files and such redundancy.
I have often thought that a compiler could just effectively concatenate all the source files of a project and compile it in one go. We have memory enough now a days.
Sounds like Spin compilers go a long way in that direction.
As processors have become thousands of times faster over the years compilers have got slower to compensate. Compiling any non trivial C++ program takes forever.
So which is it? Is OpenSpin trivial, or is ~1 second forever?
Perhaps "trivial" is the wrong word. Some time ago I built the Qt 5 libraries on a Raspberry Pi 2. It took most of the night!
Recently I built the Cockroach database on a Raspberry Pi 3. That took a couple of hours. Not C++ but Go.
Yeah, I'm not using STL. That was the compile time with Visual Studio 2015 on my Windows 10 machine (pretty beefy Core i7 6700, 32GB ram, M.2 PCIe 4x NVMe SSD drives). I haven't tested since switching to 2017, but it seems similarly fast. The NVMe SSDs make a pretty significant difference, especially since most of the compile time is I/O.
Heater,
Qt5 is a pretty huge set of libraries, and I bet 90% of the time was spent on I/O for R. Pi.