Summary
There is no documented way to express byte-oriented processing in WFL. Text is character-indexed with no byte view, Binary supports only length (it cannot be indexed, iterated, or sliced), and the 190-function native registry has no ord/chr/hex/base64/encode/decode. A byte-faithful port of a byte-oriented program is not expressible at usable speed.
Reproduction
g7_bytes.bin contains the 6 bytes 41 42 FF FE 43 44 (AB, two invalid-UTF-8 bytes, CD).
open file at "g7_bytes.bin" for reading binary as fin
wait for store payload as read binary from fin
close file fin
display "type: " with typeof of payload
display "length: " with length of payload
try:
display "index: " with payload at 0
when error:
display "index FAILED: " with error_message
end try
try:
for each byte_item in payload:
display "iter: " with byte_item
end for
when error:
display "iterate FAILED: " with error_message
end try
try:
display "slice: " with slice of payload and 0 and 2
when error:
display "slice FAILED: " with error_message
end try
try:
display "substring: " with substring of payload and 0 and 2
when error:
display "substring FAILED: " with error_message
end try
Command:
Expected
Some documented way to read the numeric value of byte k.
No documentation citation exists claiming any of these work — this is a
capability gap, not a broken runtime. Docs/04-advanced-features/file-io.md:152-155
states the position deliberately:
Text reads (read content) require valid UTF-8, so they corrupt or reject
non-text files such as fonts, images, PDFs, or compressed archives. For those,
use WFL's binary file operations, which preserve every byte exactly.
The binary section then documents exactly three operations —
read binary (:162), read N bytes (:179), and write binary accepting a
list of byte numbers (:185-197). Nothing documents reading a byte back out.
So the asymmetry is the concrete request: the byte encoder exists
(list of numbers → Binary → file), the byte decoder does not.
Actual
type: Binary
length: 6
index FAILED: Cannot index Binary with Number
iterate FAILED: Cannot iterate over Binary
slice FAILED: Error in native function: Runtime error at line 0, column 0: Expected a list, got Binary
substring FAILED: Error in native function: Runtime error at line 0, column 0: Expected text, got Binary
Exit code 0 (each failure was caught). The type checker rejects all four at
compile time as well, so this is a closed set rather than one unwired code path:
error[ERROR]: Cannot index into Binary - Expected List of Unknown but found Binary
error[ERROR]: Collection in for-each loop must be a list or map - Expected List of Unknown but found Binary
error[ERROR]: Argument 1 of builtin 'slice' expected List of Any, but found Binary
error[ERROR]: Argument 1 of builtin 'substring' expected Text, but found Binary
length of payload → 6 is correct, and Binary values compare correctly with
is equal to. Those two are the entire supported surface.
No byte primitives in the registry
Full dump of registered natives
(grep -rho 'define_native("[a-z_0-9]*"' src/ | sort -u) — 190 functions, of
which zero match byte|hex|base64|ord|chr|charcode|codepoint|encode|decode|utf8.
(secure_random_bytes produces hex text; it does not inspect bytes.)
There is also no seek: no seek token in the parser and no mention in
Docs/04-advanced-features/file-io.md, so read N bytes can only ever return a
prefix.
Text cannot carry arbitrary bytes either (documented, working as intended)
open file at "g7_bytes.bin" for reading as fin
wait for store payload as read content from fin
close file fin
→ error[ERROR]: Failed to read file: stream did not contain valid UTF-8: invalid utf-8 sequence of 1 bytes from index 2, exit 1
(src/stdlib/filesystem.rs:258).
This part is correct behaviour, not a defect — it is exactly what
file-io.md:152 says will happen, and it is included here only to show that the
Text route is closed by design, leaving Binary as the documented alternative.
The only route that works, and what it costs
Byte k can be recovered by induction: knowing bytes 0..k-1, construct each of
the 256 candidate prefixes as a number list, write binary it, read it back, and
compare against read (k+1) bytes of the target. Verified working — it correctly
decoded the first 8 bytes of a file containing 00 01 02 …:
decoded bytes: [0, 1, 2, 3, 4, 5, 6, 7]
Measured worst case on a 16-byte file of 0xFF (every byte needs all 256 trials):
22.84 s for 16 bytes ≈ 1.43 s/byte, and the cost grows with position because
the whole known prefix is rewritten for every candidate — O(n²·256) overall, with
two filesystem round trips per candidate. Extrapolated to the 630,176-byte fixture
this port needed, that is on the order of days.
(For the record: I could not confirm a separately reported figure of ~0.26 ms/byte
for an in-memory 256-entry comparison table. A table of 256 one-byte Binary
values can be built and compared quickly, but it cannot be used to decode a file,
because there is no way to extract byte k of a multi-byte Binary to compare
against it. My 1.43 s/byte figure is for the only route I could make actually work.)
Related, not duplicate
#573 (OPEN) — "Web server cannot serve binary content" — covers byte-lossless
transport through the web server. This issue is about byte-level inspection and
manipulation of values inside the language. Fixing either does not fix the other,
but they share a root: bytes are second-class.
Shape of a fix (maintainer's call)
Any one of these would close the gap; the first is the smallest:
byte at <n> of <binary> returning a Number, plus for each byte in <binary>;
- a
bytes of <binary> → list of Numbers conversion (the exact inverse of the
already-documented write binary <list of byte numbers>);
to_hex / from_hex (or base64) on Text/Binary;
ord / chr on single-character Text, which would also serve the
character-oriented cases.
Environment
- wfl --version:
WebFirst Language (WFL) version 26.8.4
- binary: system install
C:\Program Files\wfl\bin\wfl.exe
- commit: c277d8f
- OS: Windows 11 Pro 10.0.26200
- build: release
Also reproduces on the repo build (26.8.2). No .wflcfg in scope.
Context
Found while porting G:/repos/JShrink/src/JShrink/Minifier.php (a 738-line PHP
JavaScript minifier) to WFL. JShrink is byte-oriented throughout: strlen,
$input[$index], and a $char < "\x20" control-character test all operate on
bytes, and PCRE without /u classifies each byte independently.
This one shaped the port: with no byte view available, the port was written
character-based. For pure-ASCII input the two are identical, and both JShrink
parity fixtures are pure ASCII (tests/Resources/libraries/jquery_ui.js, 630,176
bytes, no byte ≥ 0x80). For non-ASCII input the ports can differ in
whitespace-elision decisions around a multi-byte character, because PHP classifies
each byte while WFL classifies the whole character. The port's parity claim is
therefore scoped to ASCII input, and that scoping is a consequence of this gap
rather than a choice. No stub was left.
Summary
There is no documented way to express byte-oriented processing in WFL.
Textis character-indexed with no byte view,Binarysupports onlylength(it cannot be indexed, iterated, or sliced), and the 190-function native registry has noord/chr/hex/base64/encode/decode. A byte-faithful port of a byte-oriented program is not expressible at usable speed.Reproduction
g7_bytes.bincontains the 6 bytes41 42 FF FE 43 44(AB, two invalid-UTF-8 bytes,CD).Command:
Expected
Some documented way to read the numeric value of byte k.
No documentation citation exists claiming any of these work — this is a
capability gap, not a broken runtime.
Docs/04-advanced-features/file-io.md:152-155states the position deliberately:
The binary section then documents exactly three operations —
read binary(:162),read N bytes(:179), andwrite binaryaccepting alist of byte numbers (
:185-197). Nothing documents reading a byte back out.So the asymmetry is the concrete request: the byte encoder exists
(list of numbers → Binary → file), the byte decoder does not.
Actual
Exit code 0 (each failure was caught). The type checker rejects all four at
compile time as well, so this is a closed set rather than one unwired code path:
length of payload→6is correct, and Binary values compare correctly withis equal to. Those two are the entire supported surface.No byte primitives in the registry
Full dump of registered natives
(
grep -rho 'define_native("[a-z_0-9]*"' src/ | sort -u) — 190 functions, ofwhich zero match
byte|hex|base64|ord|chr|charcode|codepoint|encode|decode|utf8.(
secure_random_bytesproduces hex text; it does not inspect bytes.)There is also no
seek: noseektoken in the parser and no mention inDocs/04-advanced-features/file-io.md, soread N bytescan only ever return aprefix.
Text cannot carry arbitrary bytes either (documented, working as intended)
→
error[ERROR]: Failed to read file: stream did not contain valid UTF-8: invalid utf-8 sequence of 1 bytes from index 2, exit 1(
src/stdlib/filesystem.rs:258).This part is correct behaviour, not a defect — it is exactly what
file-io.md:152says will happen, and it is included here only to show that theText route is closed by design, leaving Binary as the documented alternative.
The only route that works, and what it costs
Byte k can be recovered by induction: knowing bytes
0..k-1, construct each ofthe 256 candidate prefixes as a number list,
write binaryit, read it back, andcompare against
read (k+1) bytesof the target. Verified working — it correctlydecoded the first 8 bytes of a file containing
00 01 02 …:Measured worst case on a 16-byte file of
0xFF(every byte needs all 256 trials):22.84 s for 16 bytes ≈ 1.43 s/byte, and the cost grows with position because
the whole known prefix is rewritten for every candidate — O(n²·256) overall, with
two filesystem round trips per candidate. Extrapolated to the 630,176-byte fixture
this port needed, that is on the order of days.
(For the record: I could not confirm a separately reported figure of ~0.26 ms/byte
for an in-memory 256-entry comparison table. A table of 256 one-byte
Binaryvalues can be built and compared quickly, but it cannot be used to decode a file,
because there is no way to extract byte k of a multi-byte
Binaryto compareagainst it. My 1.43 s/byte figure is for the only route I could make actually work.)
Related, not duplicate
#573 (OPEN) — "Web server cannot serve binary content" — covers byte-lossless
transport through the web server. This issue is about byte-level inspection and
manipulation of values inside the language. Fixing either does not fix the other,
but they share a root: bytes are second-class.
Shape of a fix (maintainer's call)
Any one of these would close the gap; the first is the smallest:
byte at <n> of <binary>returning a Number, plusfor each byte in <binary>;bytes of <binary>→ list of Numbers conversion (the exact inverse of thealready-documented
write binary <list of byte numbers>);to_hex/from_hex(or base64) on Text/Binary;ord/chron single-character Text, which would also serve thecharacter-oriented cases.
Environment
WebFirst Language (WFL) version 26.8.4C:\Program Files\wfl\bin\wfl.exeAlso reproduces on the repo build (26.8.2). No
.wflcfgin scope.Context
Found while porting
G:/repos/JShrink/src/JShrink/Minifier.php(a 738-line PHPJavaScript minifier) to WFL. JShrink is byte-oriented throughout:
strlen,$input[$index], and a$char < "\x20"control-character test all operate onbytes, and PCRE without
/uclassifies each byte independently.This one shaped the port: with no byte view available, the port was written
character-based. For pure-ASCII input the two are identical, and both JShrink
parity fixtures are pure ASCII (
tests/Resources/libraries/jquery_ui.js, 630,176bytes, no byte ≥ 0x80). For non-ASCII input the ports can differ in
whitespace-elision decisions around a multi-byte character, because PHP classifies
each byte while WFL classifies the whole character. The port's parity claim is
therefore scoped to ASCII input, and that scoping is a consequence of this gap
rather than a choice. No stub was left.