New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comparison with oberonc #1
Comments
I have checked out your solution at https://github.com/lboasso/oberonc. It uses module tables 'GModtab' and 'LModtab' and creates separate entries for aliases via InsertObject. I guess I could adopt this idea, but it would add some complexity to the FPGA Oberon compiler.. |
Yes that is right. As I mentioned before this change increased the complexity of the compiler, but overall I think it was worth it. See here for the diff of when I introduced the change. Later commits fixed a few bugs. |
Luca: The new ORB9 should now in essence behave like your oberonc, i.e. any number of alias names are allowed to refer to one and the same module & the following error codes are returned:
|
I am not sure how ORB9 could really support re-exported types. You removed the "invalid import order", restriction but at the same time you don't have local and global tables to make sure you always use the primary instance of a module.
I have additional tests in here.
|
Hi Luca, I have added ORB10.Mod which re-introduces the "invalid import order" restriction. Now the following error message is produced for your test programs:
So the restriction is still there, but at least the error message is now as in FPGA Oberon. In order to eliminate the restriction itself, one would need to either (a) use a module table, like you do in your compiler or (b) somehow merge the object lists (in mod.dsc) of directly and indirectly imported modules when a module is imported directly after it has been imported indirectly. |
I think OP2 (Oberon-2 portable compiler) by R.Crelier use the 'GModtab' and 'LModtab' solution |
Multiple Modula-2 and Oberon compilers have made use of the GModtab and LModtab in various forms in the past. OP2 is one of them. |
Among the proposed solutions, I like ORB10.Mod best: it keeps the complexity to a minimum while still being in the spirit of FPGA Oberon. Also note that in the happy (and common) code path you do just one expensive string comparison. |
Luca, I have now added ORB11 and ORP12. Just as a proof of concept. It removes the restriction on import order, i.e. it is now no longer necessary that explicit imports come before re-imports. But I cannot run your test programs without heavy editing (the contain built-in WriteInt etc) |
I kept WriteInt and WriteChar and a few others for testing/bootstrapping purposes. You could add support for them in your compiler but it would take more time than to do the edit or commenting out those calls from the tests |
Luca, I have now uncommented the WriteInt, WriteChar statements. Now all modules in the list below: { compile EXCEPT for one, namely TestImport142.Mod which yields the following error. pos 103 external base type not implemented when trying to compile the declarations TYPE PtrRec1 = POINTER TO I1.Rec1; This, however, is the normal behaviour in FPGA Oberon (if I move the pointer type declarations for PtrRec0 to TestImport140 and for PtrRec1 to TestImport141, then TestImport142 also compiles) |
You made very good progress!
The new algorithm looks reasonable, maybe we can try to come up with more
tests to rule out bugs and get some confidence on the implementation.
I can help you with that
…On Fri, Feb 14, 2020, 09:16 Andreas Pirklbauer ***@***.***> wrote:
Luca,
I have now uncommented the WriteInt, WriteChar statements. Now all modules
in the list below:
{
{"TestImport00", "TestImport01"},
{"TestImport10", "TestImport11"},
{"TestImport20", "TestImport21", "TestImport22"},
{"TestImport30", "TestImport31"},
{"TestImport40", "TestImport41", "TestImport42"},
{"TestImport50", "TestImport51", "TestImport52", "TestImport53"},
{"TestImport60", "TestImport61", "TestImport62"},
{"TestImport70", "TestImport71"},
{"TestImport81", "TestImport82", "TestImport80"},
{"TestImport90", "TestImport91"},
{"TestImport100"},
{"TestImport120", "TestImport121", "TestImport122"},
{"TestImport130", "TestImport131"},
}
compile EXCEPT for one, namely TestImport142.Mod which yields the
following error.
pos 103 external base type not implemented
when trying to compile the declarations
TYPE PtrRec1 = POINTER TO I1.Rec1;
VAR r0: POINTER TO I0.Rec0;
This, however, in FPGA is the normal behaviour (if I move the pointer type
declarations for PtrRec0 to TestImport 140 and for PtrRec1 to
TestImport141, then TestImport142 also compiles)
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#1>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAVJAWZ2XV73QYOP6Z74RRDRC3GYVANCNFSM4KSG3QUA>
.
|
That would be great! You obviously have deep knowledge about this wonderfully exciting topic, having done an implementation using the GModtab approach inspired by Griesemer et al or OP2. I think we can get it down to ~20-25 lines more relative to FPGA Oberon. Which would be acceptable to me. Provided we can harden it such that it truly works in all cases. Also, now that the initial phase doesn't actually do any importing, perhaps we can take the "IF non THEN" out of ORB.ThisModule to make the code simpler and more readable. |
Hi Luca, ORB12 does the same as ORB11, but procedure "ThisModule" has been split into "Import" (called only initially when parsing the IMPORT statement) and "Reimport" (called from within InType). This makes it more readable and simpler (we're down to ~25 lines more than FPGA Oberon). It should also make it easier to design test programs that check specific cases. |
Added ORB13.Mod = a ONE-pass solution with NO restrictions on import order. This solution solves the same problem as ORB12.Mod, but does not first build the entire module list to determine upfront which modules are re-imported and which are explicitly imported. Instead, ORB13.Mod stitches together the implicit imports and later explicit ones, if needed. This means that in the event that a module M is first implicitly imported (re-imported types) and later explicitly imported (=imported in full) as well, this solution "merges" the two lists. This is rather cumbersome. The main reason is that we must simultaneously NOT invalidate any pointers to already re-imported types, and at the same time read the entire symbol file. PS: All test programs TestImport00.Mod - TestImport151.Mod that you have listed above (with TestImport142 slightly adapted to comply with FPGA Oberon), compile correctly with both ORB12/ORP12 and ORB13. |
Hi Andreas, TestCyclicImport00A TestCyclicImport00A TestCyclicImport10A I remember that I had to add extra support for it, see the comment in this commit |
Hi Luca, ORB12CheckCyclicImports (two-pass variant) and ORB13CheckCyclicImports (single pass variant) now detect cyclic imports in ALL cases, i.e. both in the case where imported or re-imported types of the involved modules are imported and also when they are not re-exported. I had to add a few lines that (effectively) do what your "module anchors" do. So this comes essentially as an after-thought to ORB12 and ORB13, and therefore cannot be recommended. This small experiment has convinced me that if one also want to check for cyclic module imports at compilation time, your "global/local module table" approach that you employ in your oberonc compiler is indeed the more appropriate one. But one could also argue that letting the module loader enter an endless recursion when cyclic module imports are present in any given module hierarchy, is also an adequate solution for the following reason: Cyclic module imports have to be actively constructed through a tricky sequence of editing and compilation steps - as you have done in your TestCyclicImport00A, etc. But normally, they just don't occur. This could be debated of course.. Here are some stats (sloc means "source lines of code", i.e. not counting empty lines): ORB00.Mod (=FPGA Oberon, not a correct and also not a full solution) None of these checks for cyclic imports. I am currently using ORB08.Mod in my own version of Oberon (Extended Oberon) at present. Of course, it is more restrictive than the other variants (for example, multiple aliases to the same module are not allowed), but simple. |
Hi Luca, Just a quick update: I have now - essentially - completed this little experiment with import aliases. In the meantime, P. Reed has provided an alternative variant (=ORB06.Mod) which is somewhat less restrictive than my originally proposed variant (=ORB07.Mod). Unlike my original variant, his variant doesn't check the two "cross-combinations" (obj.orgname # name) and (obj.name # orgname) in ORB.ThisModule. To convince myself that this is indeed "safe" to do, I have conducted a detailed case analysis which systematically checks all possible cases (see CaseAnalysis). This analysis convincingly proves that: a) Not checking the first cross-combination (obj.orgname # name) safely allows:
b) Not checking the second cross-combination (obj.name # orgname) safely allows:
Since this is still within the capabilities of what the module list data structure of the Oberon-07 FPGA RISC compiler can handle (it could not, for example, handle multiple aliases to the same module), I have now decided to adopt a slightly modified version of ORB06.Mod (which does slightly better reporting) at ORB06.Mod. Given its simplicity (only a few lines to be added to the official compiler), I use that for the time being. The restrictions seem reasonable to me (only single but not multiple aliases allowed, explicit imports must come before re-imports, cyclic imports not detected). If I ever wanted to add more features (e.g. allow multiple aliases to the same module, no restrictions on import order, cyclic imports detected in some, but not all, cases), I now also have a way to do that in the framework of the Oberon-07 FPGA Oberon compiler (e.g. ORB11.Mod). But that seemed a bit of an overkill for what I wanted to achieve. Maybe I change my mind later. Thanks for all your input on this wonderful topic of import aliases - it was greatly appreciated! |
Thanks for all the effort you put in exploring different implementation and
their relative trade offs. I wish I could have given you more feedback/help
but free time is very scarce lately.
I agree that ORB06.Mod is probably the best choice.
…On Thu, Feb 20, 2020, 23:36 Andreas Pirklbauer ***@***.***> wrote:
Hi Luca,
Just a quick update: I have now - essentially - completed this little
experiment with import aliases. In the meantime, P. Reed has provided an
alternative variant (=ORB06.Mod) which is somewhat less restrictive than my
originally proposed variant (=ORB07.Mod).
Unlike my original variant, his variant doesn't check the two
"cross-combinations" (obj.orgname # name) and (obj.name # orgname) in
ORB.ThisModule. To convince myself that this is indeed "safe" to do, I have
conducted a thorough case analysis - oh my - which systematically checks
all possible cases (see the end of the README.md file of this repository).
This proves that:
a) Not checking the first cross-combination (obj.orgname # name) safely
allows:
MODULE B10; IMPORT X := M, M := M0; END B10.
MODULE B11; IMPORT M := M0, M0 := M; END B11.
b) Not checking the second cross-combination (obj.name # orgname) safely
allows:
MODULE B12; IMPORT M0 := M, X := M0; END B12.
in the test file M.Mod. Since this is still within the capabilities of
what the module list data structure of the Oberon-07 FPGA RISC compiler can
handle (it could not, for example, handle multiple aliases to the same
module), I have now decided to adopt a slightly modified version of
ORB06.Mod (which does slightly better reporting, but otherwise is the same).
See ORB06ImprovedErrMsg.Mod
Given its simplicity (only a few lines to be added to the official
compiler), I use that for the time being. The restrictions seem reasonable
to me (only single but not multiple aliases allowed, explicit imports must
come before re-imports, cyclic imports not detected).
If I ever wanted to implement the "full" functionality (multiple aliases
to the same module, no restrictions on import order, cyclic imports
detected in all cases, even when a re-imported type is not re-exported) -
like you do in your oberonc compiler - I now also have the full
implementation for that (ORB11.Mod) at a cost of only +27 additional lines
relative to the official compiler.
But that seemed a bit of an overkill for what I wanted to achieve. Maybe I
change my mind later.
Thanks for all your input on this wonderful topic of import aliases - it
was greatly appreciated!
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#1>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAVJAW647OVQCM5ELWZIDR3RD6AAFANCNFSM4KSG3QUA>
.
|
Luca, your input was definitely helpful - it made me re-read Griesemer's paper after 3 decades and also study oberonc, which uses the GModtab/LModtab approach described in the paper. A few comments:
Although it does indeed detect ALL cyclic imports during compilation - including in the case where imported or re-imported types are NOT re-exported - it does so in a rather "unnatural" way. In essence, I retrofit the solution by adding back "module anchors" using additional entries in the module list rooted in ORB.topScope. So it comes as an afterthought. I now have to traverse this list during export, whereas you can simply access the global array GModtab. Another obvious downside of my solution is, of course, that module anchors are written multiple times, whereas you write them only once, and use a reference number later. For this and the reasons mentioned above, I added a comment that in the case where one wants to "full" solution, it is probably better to just adopt the GModtab/LModtab approach that you use in oberonc. Still, it's comforting to know that the "full" solution can be engineered with <40 lines of extra code relative to the Oberon-07 FPGA RISC compiler. That seems to be less than in oberonc (but the compilers are hard to compare, since your target is different). In any case, I have now a satisfactory solution: ORB06.Mod. I have already spent more time on this than planned (one week elapsed, one day actual). I'll now let it sit and ferment a little bit. Perhaps one day I have a clever idea, how one could turn ORB12CheckCyclicImports.Mod into a truly natural, elegant solution with <25 lines relative to Oberon-07 FPGA RISC compiler. In that case, I may reconsider it.. perhaps. Andreas |
Hi Luca, just FYI: As of today, ORB06.Mod is now live at www.projectoberon.com. This appears to be a good compromise between minimal changes and a correct implementation. The import order restriction (explicit imports have to come before re-imports) has been retained for example, which I believe is a reasonable choice. -ap |
H Andreas,
To continue our conversation started as email, I tried to compile your examples with oberonc that implements imports without the restriction on the import order.
To better test the semantic of the imports I added bodies to all the client modules. Like that we are sure that export an re-export work as expected without breaking type rules:
oberonc compiles successfully all the modules but:
IMPORT X := M0, M2, X;
I argue that M4 should compile, an alias is indeed a different name for the same object. This is true also for type alias as well. e.x.
TYPE int = INTEGER
.M5 is debatable, it still makes sense that compiles successfully.
In addition oberonc compiles with no error the following:
I am not sure how to best support these corner cases in the original FPGA Oberon. It feels like the proposed changes are a bit hacky and I wonder how many other corner cases we are not catching. Ideally we should pay the price for increased complexity in the module system and lift the restriction on the import order as I did for oberonc.
The text was updated successfully, but these errors were encountered: