-- Leo's gemini proxy

-- Connecting to bbs.geminispace.org:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini; charset=utf-8

Loading files in nForth: implementation


Having implemented the normally-compile semantics I discussed in the previous post, I must now tackle the `include` functionality to load more forth code from a file.


The problem: since nForth is now a compterpreter (it compiles code and runs/erases it to simulate interpretation), we must compile and run the loading code and the filename into the dictionary to execute the load. If we blindly load on top, the pseudo-interpretive block of code that does the loading (and the filename-string) gets stuck underneath the loaded code and cannot be removed.


To remediate this, the loading word must erase all traces of its invocation (and the filename-string) from the dictionary prior to calling the loader, presumably after opening the file.


In order to reuse the string machinery (and avoid a special-case filename parser), instead of the 'include xxxx' syntax we will opt for a more forth-like ' " xxx" included'.


Since we need to get rid of some code compiled to the dictionary, we can also reuse the commit/abort machinery built into the system. The word 'state.commit' commits all changes to the dictionary, while 'state.rollback' clears all changes made after the prior commit. The immediate word 'sync' performs a state.commit. I can also state.push and state.pop the state at the currently committed checkpoint.


Since we need to execute the loading code and not just compile it, we shall invoke the loader as:

[ ." somefile.f" included; ]

It is in square braces because we want to run it and get rid of it. The semicolon is there to remind you that anything that follows 'included;' will not be executed! In fact the rest of the line will be abandoned.


The word 'included;' is special: it is meant to be invoked inside a [ ] block, and it needs to get rid of the block prior to its execution (to avoid embedding garbage into the dictionary. It also exits the [ ] block back to the surrounding environment, because otherwise it will be too late!


For the gritty details, see the source code at tildegit (it is a forth definition in nforth.f)


=>https://tildegit.org/stack/nforth


Posted in: s/FORTH

๐Ÿš€ stack [mod]

2023-09-14 ยท 8 months ago


2 Comments โ†“


๐Ÿฆ‹ karel ยท 2023-09-15 at 06:32:

@stack: I faced a similar problem in a pet project of mine. My decision is to drop it and let the "user" (that's me) use the C preprocessor in order to gather all the code into one file. As an alternative he (me) can supply multiple code files at the command line. Disadvantage: impossible to output correct file_name:line_number error messages with CPP #includes. Advantage: separation of concerns, old-skool Unix-style. What do you think about this solution?


๐Ÿš€ stack [OP/mod] ยท 2023-09-15 at 12:12:

@karel, that is fine if it works for you, and I could've had the assembler append other forth files (it alread appends 'nforth.f'). But it turned out to be dead simple:


: included; \ (fname,cnt--)
  drop zero dup rot osopen
  pop drop \ do not return to brace
  loadh
;

And in the process I got the sync/abort logic working.


So I do append one file, nforth.f, which normally defines included; and uses it to bootstrap itself by loading nf.f. But the user can stuff anything into nforth.f, say for a single file CGI script.

-- Response ended

-- Page fetched on Sun May 19 12:48:42 2024