Skip to content

adding proper eof handling#112

Open
dallinjdahl wants to merge 1 commit into
Shen-Language:masterfrom
dallinjdahl:master
Open

adding proper eof handling#112
dallinjdahl wants to merge 1 commit into
Shen-Language:masterfrom
dallinjdahl:master

Conversation

@dallinjdahl

Copy link
Copy Markdown

This slightly refactors the toplevel to end on eof, resolving complaints about exiting shen and fitting it into a more unix-friendly environment. Care is taken to ensure that the tail-recursive nature of the loop is maintained.

@tizoc

tizoc commented Apr 9, 2026

Copy link
Copy Markdown
Member

Hi @dallinjdahl, thanks for the contribution! But this is not the right place for the change, because that error is port-specific, and the code in this repository must be portable.

Which port were you working with that requires this change? The right place for the fix is there, by overriding shen.loop so that it behaves the way you need to.

Comment thread sources/toplevel.shen
(/. E (toplevel-display-exception E)))
(loop)))
(trap-error (do (read-evaluate-print) (loop))
(/. E (toplevel-exception (error-to-string E) loop)))))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing the loop symbol and then invoking it as (C) is not portable, will work with some platforms (Common Lisp for example), but not most. You could pass (freeze loop) and then (thaw C), but many platforms will not be able to handle the tail-recursion correctly in that case.

But you could do something like:

(define is-eof-error?
  E -> (= (error-to-string E) "error: empty stream"))

(define loop
   -> (let Init (initialise_environment)
           Prompt (prompt)
           Continue (trap-error
                       (do (read-evaluate-print) true)
                       (/. E (if (is-eof-error?  E)
                                 false
                                 (do (toplevel-display-exception E)
                                     true))))
         (if Continue 
             (loop)
             skip)))

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about passing (fn loop) instead? It seems like a natural use of recursion with higher order functions, but maybe I'm misunderstanding the issue.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(fn loop) could work in some ports, but sadly it is not something you can depend on for portable code.

@dallinjdahl

Copy link
Copy Markdown
Author

The same error occurs with both the SBCL and shen-scheme ports, so I thought it would be the language-defined error for an end of file, but if it truly is port-specific, then feel-free to close this PR. The issue is that every other interpreter I've used on unix-like systems handles eof by terminating gracefully, while currently the shen top-level just spins until you call the port-specific exit command. This avoids that in general by exiting the loop portably, rather than shelling out to the underlying port.

@dallinjdahl

dallinjdahl commented Apr 10, 2026

Copy link
Copy Markdown
Author

I guess TBoS (at least v4) doesn't mention any way to determine the end of a stream, so if there's really no portable way to guard against reading the end besides reading it into a string, then this is definitely misplaced.

@tizoc

tizoc commented Apr 10, 2026

Copy link
Copy Markdown
Member

@dallinjdahl not sure about the SBCL port because it doesn't use this version of the kernel, so it doesn't have the hooks. But for Shen/Scheme you can handle this in the override for shen.toplevel-display-exception:

https://github.com/tizoc/shen-scheme/blob/ad52a9c04986086998db2857f95c28753dc676ad/src/overrides.shen#L126-L131

If the exception is the EOF exception, you can call the native exit function with ((foreign scm.exit) 0) to exit cleanly.

Other than converting the exception to a string, I think you should be able to also use the native check ((foreign scm.i/o-read-error?) E)

@tizoc

tizoc commented Apr 10, 2026

Copy link
Copy Markdown
Member

I guess TBoS (at least v4) doesn't mention any way to determine the end of a stream, so if there's really no portable way to guard against reading the end besides reading it into a string, then this is definitely misplaced.

Yes, there is no portable way. Anything related to I/O or system operations in the Shen kernel is limited to the very bare minimum.

@tizoc tizoc closed this Apr 10, 2026
@tizoc tizoc reopened this Apr 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants