Skip to content

Conversation

@ccamel
Copy link
Member

@ccamel ccamel commented Dec 23, 2025

This PR introduces an additional read_write mode to enable half-duplex transactional devices in the host's virtual file system (VFS), particularly for the axoned chain. This allows Prolog programs to use files as device interfaces supporting both command input and response output, enabling exchange and interaction with the host system.

Note: This is a delimited deviation from the ISO Prolog standard, maintaining coherence with existing I/O modes while extending functionality for VFS device interactions.

Implements ioModeReadWrite constant to support bidirectional file access.
This non-standard ISO Prolog extension allows files to be opened for both
reading and writing, enabling VFS device use cases.
@ccamel ccamel self-assigned this Dec 23, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 23, 2025

Walkthrough

This PR adds bidirectional read-write mode support to enable simultaneous reading and writing operations on the same stream. Changes include a new atomReadWrite atom symbol, ioModeReadWrite constant, updated permission checks in SetInput/SetOutput, enhanced Open function initialization, and extended stream handling with comprehensive test coverage.

Changes

Cohort / File(s) Summary
Mode & Atom Infrastructure
engine/atom.go, engine/stream.go
Introduces atomReadWrite atom symbol and ioModeReadWrite constant; extends ioMode.Term() mapping to associate read-write mode with the new atom; broadens mode handling across Flush, initRead, reset, and writer selection to treat read-write as valid alongside existing modes.
Builtin I/O Operations
engine/builtin.go
Extends SetInput and SetOutput permission checks to allow read-write mode; adds atomReadWrite to Open's mode mapping; implements special initialization for read-write mode that assigns both source and sink to the opened file and performs read initialization for bidirectional I/O.
Test Coverage
engine/builtin_test.go, engine/stream_test.go
Adds read-write stream fixture and test cases to SetInput, SetOutput, and Open flows; extends stream tests (ReadByte, ReadRune, WriteByte, WriteRune) with read-write scenarios; introduces comprehensive TestStream_ReadWrite validating bidirectional read and write operations; adds test for SetStreamPosition position-type validation.
Documentation
README.md
Documents support for read-write mode enabling half-duplex transactional devices in the host's VFS.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding read_write mode for bidirectional file I/O. It matches the primary objective of the PR and is specific and concise.
Description check ✅ Passed The description directly relates to the changeset by explaining the purpose of the read_write mode feature, its use case for VFS devices, and its application to the axoned chain.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/stream-ioModeReadWrite

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Dec 23, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

Impacted file tree graph

@@            Coverage Diff             @@
##             main      #41      +/-   ##
==========================================
+ Coverage   98.27%   98.30%   +0.03%     
==========================================
  Files          24       24              
  Lines        7055     7063       +8     
==========================================
+ Hits         6933     6943      +10     
+ Misses         90       88       -2     
  Partials       32       32              
Files with missing lines Coverage Δ
engine/atom.go 100.00% <ø> (ø)
engine/builtin.go 99.66% <100.00%> (+0.09%) ⬆️
engine/stream.go 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
engine/stream.go (1)

472-479: Consider a more efficient mapping approach.

The array literal with keyed elements creates a sparse array (potentially 1000+ elements) with most slots being zero values. While this works, it's memory-inefficient. Consider refactoring to use a switch statement or map for better performance and clarity:

🔎 Proposed refactor using switch statement
 func (m ioMode) Term() Term {
-	return [...]Term{
-		ioModeRead:      atomRead,
-		ioModeWrite:     atomWrite,
-		ioModeAppend:    atomAppend,
-		ioModeReadWrite: atomReadWrite,
-	}[m]
+	switch m {
+	case ioModeRead:
+		return atomRead
+	case ioModeWrite:
+		return atomWrite
+	case ioModeAppend:
+		return atomAppend
+	case ioModeReadWrite:
+		return atomReadWrite
+	default:
+		return nil
+	}
 }

Note: This is an existing pattern in the codebase, so addressing it is optional for this PR.

engine/builtin_test.go (1)

3691-3785: Read-write Open/4 tests are solid; consider using stream APIs instead of raw source

The new read_write subtests (can read/write, properties, SetInput/SetOutput) give good end‑to‑end coverage for Open/4 with atomReadWrite and ensure the resulting stream:

  • Can actually read and write.
  • Exposes both input and output properties.
  • Is accepted by both SetInput and SetOutput.

One minor consistency nit: in the “can read and write” subtest you call s.initRead() but then read directly via s.source.(io.Reader).Read(b) instead of going through s.buf or ReadRune like other tests. Using the Stream read helpers (initRead + io.ReadAll(s.buf) or ReadRune) would keep the test aligned with the abstraction and catch regressions in Stream’s own logic, not just the underlying io.Reader.

This is non‑blocking, but worth considering to make the test more robust.

Example change for the read path (illustrative)
-                assert.NoError(t, s.initRead())
-                b := make([]byte, 7)
-                n, err := s.source.(io.Reader).Read(b)
-                assert.NoError(t, err)
-                assert.Equal(t, 7, n)
-                assert.Equal(t, "initial", string(b))
+                assert.NoError(t, s.initRead())
+                b, err := io.ReadAll(s.buf)
+                assert.NoError(t, err)
+                assert.True(t, strings.HasPrefix(string(b), "initial"))
engine/stream_test.go (1)

729-808: Dedicated TestStream_ReadWrite nicely validates bidirectional behavior

TestStream_ReadWrite gives a focused check that a read‑write Stream backed by a single *os.File:

  • Can initialize buffered reading (initRead).
  • Exposes a valid text writer via textWriter().
  • Successfully performs both ReadRune and WriteRune.
  • Advertises both input and output in properties().

This is a good complement to the builtin‑level tests and ensures the core Stream abstraction behaves as expected in read_write mode. If you ever want to tighten it further, you could optionally assert on s.position and/or the final file contents, but it’s already effective as is.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 048be23 and a2f6e96.

📒 Files selected for processing (6)
  • README.md
  • engine/atom.go
  • engine/builtin.go
  • engine/builtin_test.go
  • engine/stream.go
  • engine/stream_test.go
🧰 Additional context used
🧬 Code graph analysis (2)
engine/stream_test.go (2)
engine/stream.go (1)
  • Stream (34-50)
engine/atom.go (1)
  • Atom (202-202)
engine/stream.go (1)
engine/term.go (1)
  • Term (12-15)
🔇 Additional comments (18)
engine/atom.go (1)

152-152: LGTM!

The new atomReadWrite atom constant is correctly defined and follows the established pattern for well-known atoms.

README.md (1)

45-45: LGTM!

The documentation clearly describes the new read_write mode feature and its intended use case for half-duplex transactional devices.

engine/stream.go (5)

271-271: LGTM!

The Flush() method correctly includes ioModeReadWrite in the permission check, allowing bidirectional streams to be flushed.


307-307: LGTM!

The initRead() and reset() functions correctly include ioModeReadWrite in their permission checks, enabling read operations on bidirectional streams.

Also applies to: 328-328


382-383: LGTM!

The properties() method correctly reports both input and output properties for read-write streams, accurately reflecting their bidirectional capabilities.


408-408: LGTM!

The textWriter() and binaryWriter() methods correctly include ioModeReadWrite in their permission checks, enabling write operations on bidirectional streams.

Also applies to: 420-420


468-469: LGTM!

The ioModeReadWrite constant is correctly defined using os.O_RDWR, which is the standard flag for bidirectional file access.

engine/builtin.go (4)

1196-1196: LGTM!

The SetInput() function correctly accepts streams in ioModeReadWrite, allowing bidirectional streams to be set as the current input stream.


1211-1211: LGTM!

The SetOutput() function correctly accepts streams in ioModeReadWrite, allowing bidirectional streams to be set as the current output stream.


1257-1260: LGTM!

The Open() function correctly maps atomReadWrite to ioModeReadWrite, enabling Prolog code to open files with the read_write mode.


1279-1282: Code implementation is correct. Tests in TestStream_ReadWrite and the read_write test in builtin_test.go already verify that position tracking and buffering work correctly when alternating between read and write operations on the same file. Setting both source and sink to the same file handle and calling initRead() is the correct approach for bidirectional I/O initialization.

engine/builtin_test.go (3)

3364-3389: Extend SetInput coverage to read_write streams

Allowing SetInput to accept a readWrite stream (and verifying vm.input is updated) correctly exercises the new ioModeReadWrite as an input-capable mode. The setup using os.Stdin/os.Stdout is fine in tests and matches existing patterns.


3402-3427: Extend SetOutput coverage to read_write streams

Similarly, adding a readWrite fixture and positive case in TestSetOutput validates that ioModeReadWrite is treated as output-capable, while existing permission-error cases remain intact.


6501-6571: Good additional edge coverage for SetStreamPosition

The new tests around SetStreamPosition/3:

  • Exercise non‑integer position values (atom, float, compound) and assert typeError(integer, _).
  • Verify that a direct *Stream with reposition: false (not just an alias‑resolved stream) correctly raises permission_error(reposition, stream, ...).

These cover important edge cases for the read_write / repositionable stream story and look correct as written.

engine/stream_test.go (4)

278-357: Treating read_write as a valid input mode for binary reads

Adding the "read-write binary" case to TestStream_ReadByte confirms that ReadByte accepts ioModeReadWrite in combination with streamTypeBinary and still updates position/endOfStream as expected. This aligns with the intended half‑duplex semantics.


359-477: Treating read_write as a valid input mode for text reads

The "read-write text" case in TestStream_ReadRune similarly ensures that ReadRune works when mode == ioModeReadWrite and streamType == streamTypeText, keeping the existing EOF and error behaviors unchanged for other modes and types.


563-611: WriteByte supports read_write; mock expectations updated appropriately

Changing the mock to .Twice() and adding the "read-write" test case verifies that WriteByte:

  • Accepts ioModeReadWrite with a binary stream.
  • Still rejects pure input mode and wrong stream type as before.

The position bookkeeping (pos: 1) is checked for both successful writes, which is useful.


613-665: WriteRune supports read_write; consistent with text semantics

The analogous changes in TestStream_WriteRune (twice‑called mock and "read-write" case) confirm that WriteRune operates correctly in ioModeReadWrite for text streams while preserving existing errors for input mode and binary streams.

@ccamel ccamel merged commit fbcaccf into main Dec 23, 2025
3 checks passed
@ccamel ccamel deleted the feat/stream-ioModeReadWrite branch December 23, 2025 16:51
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