Path: csiph.com!aioe.org!.POSTED!not-for-mail From: Rod Pemberton Newsgroups: comp.lang.forth Subject: Re: LLVM and Forth Date: Mon, 3 Apr 2017 20:39:45 -0400 Organization: Aioe.org NNTP Server Lines: 213 Message-ID: <20170403203945.034012ea@_> References: <21153661-abf5-4958-82c7-e3c442b45cb9@googlegroups.com> <065e774c-c5fb-4e68-9489-00b5357142d6@googlegroups.com> NNTP-Posting-Host: ZTD62cW/Uw8bymX5ok+0NA.user.gioia.aioe.org Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit X-Complaints-To: abuse@aioe.org X-Notice: Filtered by postfilter v. 0.8.2 Xref: csiph.com comp.lang.forth:56680 On Sun, 2 Apr 2017 06:35:25 -0700 (PDT) krishna.myneni@ccreweb.org wrote: > On Sunday, April 2, 2017 at 4:02:26 AM UTC-5, Andrew Haley wrote: > > krishna.myneni@ccreweb.org wrote: > If one were to write a Forth to C translator, how likely is it that > the translator would result in C code which is considered undefined > in the new C standard, IMO, unlikely, especially if the Forth was mostly written in high-level Forth, being built up from "primitives" like eForth. However, it is entirely possible that there are complicated interactions. Typically, eForths have 30 to 60 or so low-level words or "primitives". All the high-level words are coded in terms of the primitives. The advantage here is that the high-level words translate easily to C, and most of an eForth like Forth is coded as high-level Forth words. So, much of your work is done for you. You just need to implement the "primitives" in C and the Forth data structures, like stacks, etc. In this scenario, high-level Forth words would become a procedure call, where each procedure has a list of calls to other procedures. Forth "primitive" words would be C procedures that actually do some work on the stacks, instead of just calling other procedures. I think the only issue here would be conversion of the names for Forth words, e.g., '+' '\' etc, to procedure names which are valid in C, e.g., 'add' 'divide' etc. E.g., a high-level Forth word using a "primitive" twice: : DUP2 DUP DUP ; In C, void DUP2(void) /* high-level Forth word */ { DUP(); /* list of procedures */ DUP(); } void DUP(void) /* "primitive" Forth word */ { /* actual code to modify Forth stacks */ TOS=*SP; /* retrieve TOS value */ SP++; /* adjust stack pointer to add space for new TOS */ *SP=TOS; /* save TOS value into new TOS */ } As you can see, the high-level definition of DUP2 can be converted to C simply by inserting or replacing text. E.g., ':' becomes 'void', ';' becomes '}', the definition name has '(void) {' inserted after it, and the called Forth words have '();' inserted afterwards. The "primitive" DUP actually needs C code to do work on the Forth stacks. The functions have no parameters in C because the code uses a data stack set up in C for Forth, and not C's internal stack. Of course, the code would need to be self-contained to be a compiled program, otherwise, it's just a library of functions. I.e., you'd need to have all interactive commands saved as text to be compiled. E.g., you'd not only need the code for DUP2 but also the interactive line using DUP2 such as: : DUP2 DUP DUP ; 5 DUP2 . . . So, the "5 DUP2 . . ." would become your main() procedure in C: int main(void) { LIT(5); DUP2(); DOT(); DOT(); DOT(); } As you can see '.' is an example of the name conversion issue, i.e., '.' in Forth to DOT() in C. Implementing DOT() is left as an exercise for the reader. Also, all control-flow words in Forth, e.g., IF ELSE THEN DO LOOP BEGIN AGAIN UNTIL WHILE REPEAT etc, will map directly onto existing C control-flow constructs. So, converting Forth control-flow to C is not a problem either. One issue is that the Forth interactive commands and Forth definitions can occur in any order, so your conversion program must filter out the Forth definitions and define them first. This is an issue if Forth words are redefined. The control-flow of the Forth program will be changed by re-ordering to fit C's requirements, and C will complain about redefining a procedure. I.e., your conversion program will need to keep track of both DUP2's below and need to treat each of them as being distinct procedures: : DUP2 DUP DUP ; 5 DUP2 . . . : DUP2 DUP DUP + ; 6 DUP2 . . void DUP2_1(void) /* high-level Forth word */ { DUP(); /* list of procedures */ DUP(); } void DUP2_2(void) /* high-level Forth word */ { DUP(); /* list of procedures */ DUP(); PLUS(); } void DUP(void) /* "primitive" Forth word */ { /* actual code to modify Forth stacks */ TOS=*SP; /* retrieve TOS value */ SP++; /* adjust stack pointer to add space for new TOS */ *SP=TOS; /* save TOS value into new TOS */ } void PLUS(void) { TOS=*SP SP--; *SP+=TOS; } int main(void) { LIT(5); DUP2_1(); DOT(); DOT(); DOT(); LIT(6); DUP2_2(); DOT(); DOT(); } As you can see, both DUP2's are defined first, kept distinct, and then the respective interactive commands for each DUP2 are called in main(), thereby preserving the correct control-flow of the program. Obviously, this will require that you implement a Forth dictionary as a look-up table to determine the correct DUP2 to use during the conversion ... I.e., when "6 DUP2" is seen, the look-up will need to correctly return DUP2_2() for DUP2 instead of DUP2_1(). Depending on how you convert code for string words, you may also need to filter out strings and define them first too. To prevent naming collisions in C, it may make sense to convert all Forth procedure names to numbers and emit a comment as to what the original name was. The 'X' is because C requires a first character of a procedure name to be alphabet character or an underscore. A counter would be incremented upon each new colon-def in Forth in order to generate the numeric names. void X0002(void) /* DUP */ { ... } void X0003(void) /* DUP */ { ... } int main(void) { X0001(5); /* LIT */ X0002(); /* DUP */ ... X0001(6); /* LIT */ X0003(); /* DUP */ ... } > i.e. how problematic would "nasal demons" be > in the translated code? AISI, one operation in Forth which might be an issue is the CREATE ... DOES> construct. It's somewhat object-oriented. I haven't attempted DOES> to convert DOES> code either by myself or by program conversions. However, I suspect the conversion is rather straight forward, as DOES> basically redirects the list of procedures which are called to another set. Another area of problems might be words with different run-time versus compile-time actions, etc. The word DUP2 in the example obviously works the same way for both, as will most "primitives". So, I suspect much of the compile-time actions will be taken care of by the code conversion to C. The resulting C code obviously should be the run-time actions for the Forth code, with compile-time actions taken care of in the code. I hope that helps. I'm sure that's a rather rudimentary presentation, but you'll discover and solve other issues as you work on the problem. So, I'd guess that solving those basic issues above, and implementing the "primitives" will solve most of the conversion problems, especially if the high-level words are constructed using "primitives" like eForth. Rod Pemberton -- All it takes for humanity to conquer the world's toughest problems is to hope, to believe, to share, and to do, in cooperation.