all repos — hex @ eaaeed8727ca2c5254b95448e4f73be256c83053

A tiny, minimalist, slightly-esoteric concatenative programming lannguage.

Merge branch 'next'
h3rald h3rald@h3rald.com
Wed, 04 Mar 2026 19:38:07 +0000
commit

eaaeed8727ca2c5254b95448e4f73be256c83053

parent

0b4f78d3ff22787d5629f09861f21c5a224432f4

M CHANGELOG.mdCHANGELOG.md

@@ -1,6 +1,7 @@

<article> <h2>Changelog</h2> <ul> +<li><a href="#v0.7.0">v0.7.0</a></li> <li><a href="#v0.6.0">v0.6.0</a></li> <li><a href="#v0.5.0">v0.5.0</a></li> <li><a href="#v0.4.1">v0.4.1</a></li>

@@ -9,6 +10,28 @@ <li><a href="#v0.3.0">v0.3.0</a></li>

<li><a href="#v0.2.0">v0.2.0</a></li> <li><a href="#v0.1.0">v0.1.0</a></li> </ul> +<h3 id="v0.7.0">v0.7.0 &mdash; 2026-03-04</h3> + +<h4>Breaking Changes</h4> +<ul> + <li>Changed integer literal notation from <code>0x</code> prefix to <code>$</code> prefix (e.g., <code>$1f</code> instead of <code>0x1f</code>).</li> +</ul> + +<h4>Fixes</h4> +<ul> + <li>Fixed several memory leaks in the <a href="https://hex.2c.fyi/spec#if-symbol">if</a>, <a href="https://hex.2c.fyi/spec#try-symbol">try</a>, <a href="https://hex.2c.fyi/spec#error-symbol">error</a>, <a href="https://hex.2c.fyi/spec#args-symbol">args</a>, <a href="https://hex.2c.fyi/spec#get-symbol">get</a>, and <a href="https://hex.2c.fyi/spec#run-symbol">run</a> symbols.</li> + <li>Fixed memory leaks in bytecode interpretation of integers, strings, and native symbols.</li> + <li>Fixed memory leaks in CLI argument handling.</li> + <li>Fixed a potential crash in <a href="https://hex.2c.fyi/spec#equal-symbol">==</a> when comparing symbols with non-symbol types.</li> + <li>Fixed the <a href="https://hex.2c.fyi/spec#run-symbol">run</a> symbol to use dynamically allocated buffers instead of fixed-size buffers for command output.</li> + <li>Fixed bytecode string length decoding loop.</li> + <li>Fixed bytecode user symbol index to use 2-byte little-endian encoding.</li> +</ul> + +<h4>Chores</h4> +<ul> + <li>Increased test coverage.</li> +</ul> <h3 id="v0.6.0">v0.6.0 &mdash; 2025-11-09</h3> <h4>Breaking Changes</h4>

@@ -22,7 +45,7 @@ <li>Added symbol <a href="https://hex.2c.fyi/spec#timestamp-symbol">timestamp</a>.</li>

<li>Implemented the possibility to load additional <code>.hex</code> or <code>.hbx</code> files at startup by using the <code>-l, --load</code> option. </li> - <li>Implemented a <code>utils.hex</code> library as part of the <a href="https://hex.2c.fyi/lib/">Standard Library</a>, + <li>Implemented a <code>utils.hex</code> library as part of the new <a href="https://hex.2c.fyi/lib/">Standard Library</a>, containing additional utility symbols (loaded automatically in the playground).</li> </ul>

@@ -34,7 +57,7 @@ </ul>

<h4>Chores</h4> <ul> - <li>Added tests for <code>utils.hex</code></li> + <li>Added tests for <code>utils.hex</code>.</li> <li>Implemented a basic <code>doc.hex</code> script to generate reference documentation from hex files.</li> </ul> <h3 id="v0.5.0">v0.5.0 &mdash; 2025-04-08</h3>

@@ -148,7 +171,7 @@ <li>A multi-platform executable for the <em>hex</em> interpreter.</li>

<li>Integrated REPL.</li> <li>Integrated help and manual.</li> <li>Debug mode.</li> - <li>0x40 (64) native symbols.</li> + <li>$40 (64) native symbols.</li> <li>Support for 32bit hexadecimal integers, strings, and quotations (lists).</li> <li>A complete <a href="https://hex.2c.fyi">web site</a> with more documentation and even an interactive playground. </li>
M lib/utils.hexlib/utils.hex

@@ -24,7 +24,7 @@ ;;;; These symbols are used for controlling the flow of execution in a program.

;;; unless ;; q1 q2 -> * -;; If %:q1%% pushes 0x0 on the stack, dequotes %:q2%%. +;; If %:q1%% pushes $0 on the stack, dequotes %:q2%%. ( () swap

@@ -33,7 +33,7 @@ ) "unless" ::

;;; when ;; q1 q2 -> * -;; If %:q1%% pushes 0x1 on the stack, dequotes %:q2%%. +;; If %:q1%% pushes $1 on the stack, dequotes %:q2%%. ( () if

@@ -53,7 +53,7 @@ ;;; each

;; q1 q2 -> * ;; Applies %:q2%% to each element of %:q1%%. ( - (0x0) cat map drop + ($0) cat map drop ) "each" :: ;;; filter

@@ -84,10 +84,10 @@ (

"_ins_index" : "_ins_item" : "_ins_list" : - 0x0 "_ins_c" : + $0 "_ins_c" : () "_ins_result" : _ins_list len "_ins_len" : - (_ins_index _ins_len >= _ins_index 0x0 < or) + (_ins_index _ins_len >= _ins_index $0 < or) ("[symbol ins] Index out of bounds" throw) when (_ins_c _ins_len <)

@@ -96,7 +96,7 @@ (_ins_c _ins_index ==)

(_ins_result _ins_item ' cat "_ins_result" :) when _ins_result _ins_list _ins_c get ' cat "_ins_result" : - _ins_c 0x1 + "_ins_c" : + _ins_c $1 + "_ins_c" : ) while _ins_result

@@ -113,14 +113,14 @@ ;; q -> a

;; Pushes the maximum item of q on the stack. ( "_max_list" : - _max_list 0x0 get "_max_result" : - 0x1 "_max_c" : + _max_list $0 get "_max_result" : + $1 "_max_c" : (_max_c _max_list len <) ( (_max_result _max_list _max_c get <) (_max_list _max_c get "_max_result" :) when - _max_c 0x1 + "_max_c" : + _max_c $1 + "_max_c" : ) while _max_result

@@ -134,14 +134,14 @@ ;; q -> a

;; Pushes the minimum item of q on the stack. ( "_min_list" : - _min_list 0x0 get "_min_result" : - 0x1 "_min_c" : + _min_list $0 get "_min_result" : + $1 "_min_c" : (_min_c _min_list len <) ( (_min_result _min_list _min_c get >) (_min_list _min_c get "_min_result" :) when - _min_c 0x1 + "_min_c" : + _min_c $1 + "_min_c" : ) while _min_result

@@ -156,11 +156,11 @@ ;; Removes the last item at the end of %:q1%%.

( "_pop_q" : () "_pop_res" : - 0x0 "_pop_c" : - (_pop_c _pop_q len 0x1 - <) + $0 "_pop_c" : + (_pop_c _pop_q len $1 - <) ( _pop_res _pop_q _pop_c get push "_pop_res" : - _pop_c 0x1 + "_pop_c" : + _pop_c $1 + "_pop_c" : ) while _pop_res

@@ -184,16 +184,16 @@ (

"_rem_index" : "_rem_list" : () "_rem_result" : - (_rem_index _rem_list len >= _rem_index 0x0 < or) + (_rem_index _rem_list len >= _rem_index $0 < or) ("[symbol rem] Index out of bounds" throw) when - 0x0 "_rem_c" : + $0 "_rem_c" : (_rem_c _rem_list len <) ( (_rem_c _rem_index !=) (_rem_result _rem_list _rem_c get ' cat "_rem_result" :) when - _rem_c 0x1 + "_rem_c" : + _rem_c $1 + "_rem_c" : ) while _rem_result

@@ -208,12 +208,12 @@ ;; q1 -> q2

;; Reverses the order of the items in a quotation. ( "_reverse_list" : - _reverse_list len 0x1 - "_reverse_c" : + _reverse_list len $1 - "_reverse_c" : () "_reverse_result" : - (_reverse_c 0x0 >=) + (_reverse_c $0 >=) ( _reverse_result _reverse_list _reverse_c get ' cat "_reverse_result" : - _reverse_c 0x1 - "_reverse_c" : + _reverse_c $1 - "_reverse_c" : ) while _reverse_result

@@ -230,17 +230,17 @@ "_set_index" :

"_set_item" : "_set_list" : () "_set_result" : - (_set_index _set_list len >= _set_index 0x0 < or) + (_set_index _set_list len >= _set_index $0 < or) ("[symbol set] Index out of bounds" throw) when - 0x0 "_set_c" : + $0 "_set_c" : (_set_c _set_list len <) ( (_set_c _set_index ==) (_set_result _set_item ' cat "_set_result" :) (_set_result _set_list _set_c get ' cat "_set_result" :) if - _set_c 0x1 + "_set_c" : + _set_c $1 + "_set_c" : ) while _set_result

@@ -253,31 +253,31 @@ ) "set" ::

;;; sort ;; q1 q2 -> q3 -;; Sorts the items of q1 based on the comparison quotation q2 (must push 0x0 or 0x1 on the stack). +;; Sorts the items of q1 based on the comparison quotation q2 (must push $0 or $1 on the stack). ( "_sort_check" : "_sort_list" : _sort_list len "_sort_n" : - 0x0 "_sort_i" : - 0x1 "_sort_swapped" : + $0 "_sort_i" : + $1 "_sort_swapped" : (_sort_swapped) ( - 0x0 "_sort_swapped" : - 0x0 "_sort_i" : - (_sort_i _sort_n 0x1 - <) + $0 "_sort_swapped" : + $0 "_sort_i" : + (_sort_i _sort_n $1 - <) ( _sort_list _sort_i get "_sort_current" : - _sort_list _sort_i 0x1 + get "_sort_next" : + _sort_list _sort_i $1 + get "_sort_next" : (_sort_current _sort_next _sort_check .) ( _sort_list - _sort_current _sort_i 0x1 + set + _sort_current _sort_i $1 + set _sort_next _sort_i set "_sort_list" : - 0x1 "_sort_swapped" : + $1 "_sort_swapped" : ) when - _sort_i 0x1 + "_sort_i" : + _sort_i $1 + "_sort_i" : "_sort_current" # "_sort_next" # )

@@ -306,10 +306,10 @@ ;; Execute quotation %:q%% %:i%% times.

( "_times_i" : "_times_q" : - (_times_i 0x0 >) + (_times_i $0 >) ( _times_q . - _times_i 0x1 - "_times_i" : + _times_i $1 - "_times_i" : ) while "_times_q" #

@@ -322,14 +322,14 @@ ;; Removes the first item from %:q1%% and pushes it on the stack along with %:q2%% containing the rest of the items of %:q1%%.

( "_uncons_list" : () "_uncons_result" : - 0x1 "_uncons_c" : + $1 "_uncons_c" : (_uncons_c _uncons_list len <) ( _uncons_result _uncons_list _uncons_c get push "_uncons_result" : - _uncons_c 0x1 + "_uncons_c" : + _uncons_c $1 + "_uncons_c" : ) while - _uncons_list 0x0 get ; Push the first item + _uncons_list $0 get ; Push the first item _uncons_result ; Push the rest of the items "_uncons_list" # "_uncons_result" #

@@ -343,15 +343,15 @@ ;; Removes the first item from %:q1%% and pushes it on the stack along with %:q2%% containing the rest of the items of %:q1%%.

( "_unswons_list" : () "_unswons_result" : - 0x1 "_unswons_c" : + $1 "_unswons_c" : (_unswons_c _unswons_list len <) ( _unswons_result _unswons_list _unswons_c get push "_unswons_result" : - _unswons_c 0x1 + "_unswons_c" : + _unswons_c $1 + "_unswons_c" : ) while _unswons_result ; Push the rest of the items - _unswons_list 0x0 get ; Push the first item + _unswons_list $0 get ; Push the first item "_unswons_list" # "_unswons_result" # "_unswons_c" #

@@ -381,14 +381,14 @@ ;;;; These additional symbols are used for manipulating strings.

;;; begins ;; s1 s2 -> s3 -;; Pushes $0x1$$ on the stack if %:s1%% begins with %:s2%%, or $0x0$$ otherwise. +;; Pushes $!1$$ on the stack if %:s1%% begins with %:s2%%, or $!0$$ otherwise. ( - index 0x0 == + index $0 == ) "begins" :: ;;; ends ;; s1 s2 -> s3 -;; Pushes $0x1$$ on the stack if %:s1%% ends with %:s2%%, or $0x0$$ otherwise. +;; Pushes $!1$$ on the stack if %:s1%% ends with %:s2%%, or $!0$$ otherwise. ( "_ends_suffix" : "_ends_s" :

@@ -405,14 +405,14 @@ ;; Substitutes %:$\{0\}%% to %%$\{9\}%% placeholders in %:s1%% with items in %:q%%.

( "_fmt_q" : "_fmt_s" : - 0x0 "_fmt_c" : - (_fmt_q len 0xa >) + $0 "_fmt_c" : + (_fmt_q len $a >) ("[symbol fmt] Only a maximum of 10 placeholders are supported" throw) when (_fmt_c _fmt_q len <) ( _fmt_s "${" _fmt_c str cat "}" cat _fmt_q _fmt_c get gsub "_fmt_s" : - _fmt_c 0x1 + "_fmt_c" : + _fmt_c $1 + "_fmt_c" : ) while _fmt_s

@@ -428,7 +428,7 @@ (

"_gsub_rep" : "_gsub_src" : "_gsub_text" : - (_gsub_text _gsub_src index 0x0 >=) + (_gsub_text _gsub_src index $0 >=) (_gsub_text _gsub_src _gsub_rep sub "_gsub_text" :) while _gsub_text

@@ -445,13 +445,13 @@ "_slice_end" :

"_slice_start" : "_slice_str" : "" "_slice_result" : - (_slice_end _slice_str len >= _slice_start 0x0 < or) + (_slice_end _slice_str len >= _slice_start $0 < or) ("[symbol slice] Index out of bounds" throw) when (_slice_start _slice_end <=) ( _slice_result _slice_str _slice_start get cat "_slice_result" : - _slice_start 0x1 + "_slice_start" : + _slice_start $1 + "_slice_start" : ) while _slice_result
M releases/0.1.0.htmlreleases/0.1.0.html

@@ -6,7 +6,7 @@ <li>A multi-platform executable for the <em>hex</em> interpreter.</li>

<li>Integrated REPL.</li> <li>Integrated help and manual.</li> <li>Debug mode.</li> - <li>0x40 (64) native symbols.</li> + <li>$40 (64) native symbols.</li> <li>Support for 32bit hexadecimal integers, strings, and quotations (lists).</li> <li>A complete <a href="https://hex.2c.fyi">web site</a> with more documentation and even an interactive playground. </li>
A releases/0.7.0.html

@@ -0,0 +1,22 @@

+<h3 id="v0.7.0">v0.7.0 &mdash; 2026-03-04</h3> + +<h4>Breaking Changes</h4> +<ul> + <li>Changed integer literal notation from <code>0x</code> prefix to <code>$</code> prefix (e.g., <code>$1f</code> instead of <code>0x1f</code>).</li> +</ul> + +<h4>Fixes</h4> +<ul> + <li>Fixed several memory leaks in the {{sym-if}}, {{sym-try}}, {{sym-error}}, {{sym-args}}, {{sym-get}}, and {{sym-run}} symbols.</li> + <li>Fixed memory leaks in bytecode interpretation of integers, strings, and native symbols.</li> + <li>Fixed memory leaks in CLI argument handling.</li> + <li>Fixed a potential crash in {{sym-==}} when comparing symbols with non-symbol types.</li> + <li>Fixed the {{sym-run}} symbol to use dynamically allocated buffers instead of fixed-size buffers for command output.</li> + <li>Fixed bytecode string length decoding loop.</li> + <li>Fixed bytecode user symbol index to use 2-byte little-endian encoding.</li> +</ul> + +<h4>Chores</h4> +<ul> + <li>Increased test coverage.</li> +</ul>
M scripts/doc.hexscripts/doc.hex

@@ -1,17 +1,17 @@

#!/usr/bin/env hex "./lib/utils.hbx" read "utils.hbx" ! -(args len 0x3 <) - ("ERROR: no input file specified." puts 0x1 exit) +(args len $3 <) + ("ERROR: no input file specified." puts $1 exit) when -args 0x2 get "INFILE" : +args $2 get "INFILE" : -(args len 0x4 <) - ("ERROR: no output file specified." puts 0x1 exit) +(args len $4 <) + ("ERROR: no output file specified." puts $1 exit) when -args 0x3 get "OUTFILE" : +args $3 get "OUTFILE" : "*** Processing ${0}..." (INFILE) fmt puts
M scripts/test.hexscripts/test.hex

@@ -4,27 +4,27 @@ "./lib/utils.hbx" read "utils.hbx" !

; --- Globals -0x0 "TEST-COUNT" : -0x0 "SUCCESSES" : -0x0 "FAILURES" : +$0 "TEST-COUNT" : +$0 "SUCCESSES" : +$0 "FAILURES" : () "ERRORS" : () "FAILS" : -0x0 "RESULT" : +$0 "RESULT" : ; --- Utilities ( "_test_current" : - TEST-COUNT 0x1 + "TEST-COUNT" : + TEST-COUNT $1 + "TEST-COUNT" : ( (_test_current .) ( "." print - SUCCESSES 0x1 + "SUCCESSES" : + SUCCESSES $1 + "SUCCESSES" : ) ( "x" print - FAILURES 0x1 + "FAILURES" : + FAILURES $1 + "FAILURES" : FAILS TEST-COUNT dec ' cat "FAILS" : ) if

@@ -33,7 +33,7 @@ (

; Store error ERRORS " - Test #" TEST-COUNT dec cat ": " cat error cat ' cat "ERRORS" : "x" print - FAILURES 0x1 + "FAILURES" : + FAILURES $1 + "FAILURES" : FAILS TEST-COUNT dec ' cat "FAILS" : ) try

@@ -43,76 +43,76 @@

; --- Test Definitions ( - (0x1 "a" : a 0x1 ==) - ("a" # 0x1) - ("aaa" type "string" == 0x1 type "integer" == () type "quotation" == and and) - ((0x1 0x2 +) . 0x3 ==) + ($1 "a" : a $1 ==) + ("a" # $1) + ("aaa" type "string" == $1 type "integer" == () type "quotation" == and and) + (($1 $2 +) . $3 ==) ;4 - ("0x2 0x2 -" "test.hex" ! 0x0 ==) - (0x4 0x5 + 0x9 ==) - (0x5 0x3 - 0x2 ==) - (0x5 0x2 * 0xa ==) + ("$2 $2 -" "test.hex" ! $0 ==) + ($4 $5 + $9 ==) + ($5 $3 - $2 ==) + ($5 $2 * $a ==) ;8 - (0x5 0x2 / 0x2 ==) - (0x4 0x2 % 0x0 ==) - (0x10101 0x01010 & 0x0 ==) - (0x10111 0x01000 | 0x11111 ==) + ($5 $2 / $2 ==) + ($4 $2 % $0 ==) + ($10101 $01010 & $0 ==) + ($10111 $01000 | $11111 ==) ;12 - (0x1 0x1 ^ 0x0 ==) - (0x1 ~ 0xfffffffe ==) - (0x1 0x4 << 0x10 ==) - (0x10 0x4 >> 0x1 ==) + ($1 $1 ^ $0 ==) + ($1 ~ $fffffffe ==) + ($1 $4 << $10 ==) + ($10 $4 >> $1 ==) ;16 - ("10" int 0x10 ==) - (0x10 str "10" ==) - (0xa dec "10" ==) - ("10" hex 0xa ==) + ("10" int $10 ==) + ($10 str "10" ==) + ($a dec "10" ==) + ("10" hex $a ==) ;20 ("aaa" "aaa" ==) - (0x20 0x20 ==) - (0x21 0x22 !=) + ($20 $20 ==) + ($21 $22 !=) ("abba" "aaa" !=) ;24 - (0x2 0x1 >) - (0x2 0x2 >=) - (0x2 0x3 <) - (0x3 0x3 <=) + ($2 $1 >) + ($2 $2 >=) + ($2 $3 <) + ($3 $3 <=) ;28 - (0x2 0x3 and) - (0x1 0x0 or) - (0x1 0x0 xor) - (0x1 0x1 xor not) + ($2 $3 and) + ($1 $0 or) + ($1 $0 xor) + ($1 $1 xor not) ;32 ((() not) (error) try "[symbol not] Integer required" ==) - ((() 0x1 xor) (error) try "[symbol xor] Two integers required" ==) - ("aaaaa" len 0x5 ==) + ((() $1 xor) (error) try "[symbol xor] Two integers required" ==) + ("aaaaa" len $5 ==) ("hello" " world" cat "hello world" ==) ;36 - ((0x1 "a") ("b") cat (0x1 "a" "b") ==) - ((0x1 0x3 puts "aaa") len 0x4 ==) - ((0x2 0x3 0x4) 0x2 get 0x4 ==) - ("abcde" 0x1 get "b" ==) + (($1 "a") ("b") cat ($1 "a" "b") ==) + (($1 $3 puts "aaa") len $4 ==) + (($2 $3 $4) $2 get $4 ==) + ("abcde" $1 get "b" ==) ;40 - ("a/b/c" "t-path" : (t-path "/" index 0x0 >=) (t-path "/" "\\" sub "t-path" :) while t-path "a\\b\\c" == "t-path" #) + ("a/b/c" "t-path" : (t-path "/" index $0 >=) (t-path "/" "\\" sub "t-path" :) while t-path "a\\b\\c" == "t-path" #) ("assets\"" "\"" "/" sub "assets/" ==) (("" () cat) (error) try "[symbol cat] Two quotations or two strings required" ==) - ((0x4 len) (error) try "[symbol len] Quotation or string required" ==) + (($4 len) (error) try "[symbol len] Quotation or string required" ==) ;44 - ("this is a test" "is" index 0x2 ==) - (("a" 0x1 "b") "b" index 0x2 ==) - ("abc" "d" index 0xffffffff ==) + ("this is a test" "is" index $2 ==) + (("a" $1 "b") "b" index $2 ==) + ("abc" "d" index $ffffffff ==) (("0" "0" "0" "0") "." join "0.0.0.0"==) ;48

@@ -122,104 +122,104 @@ ("test" "test.txt" write "test.txt" read "test" ==)

("!" "test.txt" append "test.txt" read "test!" ==) ;52 - ("rm test.txt" exec 0x0 ==) - ("rm test.txt" run 0x0 get 0x1 ==) - ("ls web/contents" run 0x1 get "\n" split len 0x8 ==) - (args 0x1 get "scripts/test.hbx" ==) + ("rm test.txt" exec $0 ==) + ("rm test.txt" run $0 get $1 ==) + ("ls web/contents" run $1 get "\n" split len $8 ==) + (args $1 get "scripts/test.hbx" ==) ;56 - ((args len 0x2 ==) ("two") ("no") if "two" ==) - ((dup *) "square" :: 0x2 square 0x4 == "square" #) - (0x1 "tmp-a" : (tmp-a 0x3 <) (tmp-a 0x1 + "tmp-a" :) while tmp-a 0x3 ==) - (symbols len 0x65 ==) + ((args len $2 ==) ("two") ("no") if "two" ==) + ((dup *) "square" :: $2 square $4 == "square" #) + ($1 "tmp-a" : (tmp-a $3 <) (tmp-a $1 + "tmp-a" :) while tmp-a $3 ==) + (symbols len $65 ==) ;60 - ((0x2 0x0 /) (error "[symbol /] Division by zero" ==) try) + (($2 $0 /) (error "[symbol /] Division by zero" ==) try) (error "" ==) ("a" ' ' ' ((("a"))) ==) - ((0x1 0x2 0x3) (dup dup * *) map (0x1 0x8 0x1b) ==) + (($1 $2 $3) (dup dup * *) map ($1 $8 $1b) ==) ;64 - (0x2 dup stack (0x2 0x2) ==) - ((dup *) "square" :: 0x3 square 0x9 == "square" #) + ($2 dup stack ($2 $2) ==) + ((dup *) "square" :: $3 square $9 == "square" #) (("test" throw) (error "test" ==) try) - (0x1 0x2 swap drop drop drop stack (0x2) == swap drop) ; Also make sure to clear the stack polluted with #65 + ($1 $2 swap drop drop drop stack ($2) == swap drop) ; Also make sure to clear the stack polluted with #65 ;68 (("aaa" "puts" :) (error) try "[symbol :] Failed to store symbol 'puts'" ==) (("puts" #) (error) try "[symbol #] Cannot free native symbol 'puts'" ==) - (("aaa" 0x2 :) (error) try "[symbol :] Symbol name must be a string" ==) - ((0x2 #) (error) try "[symbol #] Symbol name must be a string" ==) + (("aaa" $2 :) (error) try "[symbol :] Symbol name must be a string" ==) + (($2 #) (error) try "[symbol #] Symbol name must be a string" ==) ;72 (("puts" .) (error) try "[symbol .] Quotation required" ==) (((puts) "test.hex" !) (error) try "[symbol !] Quotation must contain only integers" ==) - (("3" 0x3 +) (error) try "[symbol +] Two integers required" ==) - (("3" 0x3 -) (error) try "[symbol -] Two integers required" ==) + (("3" $3 +) (error) try "[symbol +] Two integers required" ==) + (("3" $3 -) (error) try "[symbol -] Two integers required" ==) ;76 - ((0x2 0x3 0x3) (0x2 0x3) > 0x1 ==) - ((0x2 0x3) (0x2 0x1) > 0x1 ==) - ((test abc) (test cde) < 0x1 ==) - (("test" "abc") ("test" "abc") <= 0x1 ==) + (($2 $3 $3) ($2 $3) > $1 ==) + (($2 $3) ($2 $1) > $1 ==) + ((test abc) (test cde) < $1 ==) + (("test" "abc") ("test" "abc") <= $1 ==) ;80 ("hello" "" split ("h" "e" "l" "l" "o") ==) - (("3" 0x3 /) (error) try "[symbol /] Two integers required" ==) - ((0x4 0x0 %) (error) try "[symbol %] Division by zero" ==) - ((() 0x3 %) (error) try "[symbol %] Two integers required" ==) + (("3" $3 /) (error) try "[symbol /] Two integers required" ==) + (($4 $0 %) (error) try "[symbol %] Division by zero" ==) + ((() $3 %) (error) try "[symbol %] Two integers required" ==) ;84 - ((() 0x3 &) (error) try "[symbol &] Two integers required" ==) - ((0x2 "" |) (error) try "[symbol |] Two integers required" ==) + ((() $3 &) (error) try "[symbol &] Two integers required" ==) + (($2 "" |) (error) try "[symbol |] Two integers required" ==) ((() "" ^) (error) try "[symbol ^] Two integers required" ==) - ((() 0x1 >>) (error) try "[symbol >>] Two integers required" ==) + ((() $1 >>) (error) try "[symbol >>] Two integers required" ==) ;88 - ((() 0x1 <<) (error) try "[symbol <<] Two integers required" ==) + ((() $1 <<) (error) try "[symbol <<] Two integers required" ==) (("" ~) (error) try "[symbol ~] Integer required" ==) - ((0x5 int) (error) try "[symbol int] String representing a hexadecimal integer required" ==) - (((0x3) int) (error) try "[symbol int] String representing a hexadecimal integer required" ==) + (($5 int) (error) try "[symbol int] String representing a hexadecimal integer required" ==) + ((($3) int) (error) try "[symbol int] String representing a hexadecimal integer required" ==) ;92 (("5" str) (error) try "[symbol str] Integer required" ==) ((("aaa") str) (error) try "[symbol str] Integer required" ==) (("10" dec) (error) try "[symbol dec] Integer required" ==) - ((0x5 hex) (error) try "[symbol hex] String representing a decimal integer required" ==) + (($5 hex) (error) try "[symbol hex] String representing a decimal integer required" ==) ;96 - ("0x2" 0x2 == 0x0 ==) - ("0x2" (0x2) !=) - ((0x3 "aaa") ("aaa" 0x5) > 0x0 ==) - (("aaa" 0x5) ("aaa" 0x5 0x4) >= 0x0 ==) + ("$2" $2 == $0 ==) + ("$2" ($2) !=) + (($3 "aaa") ("aaa" $5) > $0 ==) + (("aaa" $5) ("aaa" $5 $4) >= $0 ==) ;100 - ((test "aaa") (test "aaa" 0x5) < 0x1 ==) - (("aaa" 0x5) ("" "aaa" 0x5) <= 0x1 ==) + ((test "aaa") (test "aaa" $5) < $1 ==) + (("aaa" $5) ("" "aaa" $5) <= $1 ==) (("a" "b" and) (error) try "[symbol and] Two integers required" ==) - ((0x1 "a" or) (error) try "[symbol or] Two integers required" ==) + (($1 "a" or) (error) try "[symbol or] Two integers required" ==) ;104 - (("abc" 0x3 get) (error) try "[symbol get] Index out of range" ==) - (((0x2) 0x2 get) (error) try "[symbol get] Index out of range" ==) - ((() 0x0 get) (error) try "[symbol get] Index out of range" ==) - ((0x4 0x4 get) (error) try "[symbol get] Quotation or string required" ==) + (("abc" $3 get) (error) try "[symbol get] Index out of range" ==) + ((($2) $2 get) (error) try "[symbol get] Index out of range" ==) + ((() $0 get) (error) try "[symbol get] Index out of range" ==) + (($4 $4 get) (error) try "[symbol get] Quotation or string required" ==) ;108 (("abc" "b" get) (error) try "[symbol get] Index must be an integer" ==) - (((0x4 "aa" test) "b" get) (error) try "[symbol get] Index must be an integer" ==) - ("a" ord 0x61 ==) - (0x61 chr "a" ==) + ((($4 "aa" test) "b" get) (error) try "[symbol get] Index must be an integer" ==) + ("a" ord $61 ==) + ($61 chr "a" ==) ;112 - ("=" ord 0x3d ==) - (0x3d chr "=" ==) - (0xffffffff dec "-1" ==) - (0x80 chr "" ==) + ("=" ord $3d ==) + ($3d chr "=" ==) + ($ffffffff dec "-1" ==) + ($80 chr "" ==) ;116 - ("abc" ord 0xffffffff ==) - ((0x56 ord) (error) try "[symbol ord] String required" ==) + ("abc" ord $ffffffff ==) + (($56 ord) (error) try "[symbol ord] String required" ==) (("t" chr) (error) try "[symbol chr] Integer required" ==) ((() chr) (error) try "[symbol chr] Integer required" ==) ;120

@@ -231,62 +231,62 @@ "test1.txt" read _str == "_str" #

"rm test1.txt" exec drop ) (("a" not) (error) try "[symbol not] Integer required" ==) - ((0x1 () xor) (error) try "[symbol xor] Two integers required" ==) + (($1 () xor) (error) try "[symbol xor] Two integers required" ==) ((() "aaa" cat) (error) try "[symbol cat] Two quotations or two strings required" ==) ;124 - ((0x4 () cat) (error) try "[symbol cat] Two quotations or two strings required" ==) - ((0x4 len) (error) try "[symbol len] Quotation or string required" ==) - ((0x1 0x4 index) (error) try "[symbol index] Quotation or string required" ==) - ((("a" 0x2 "b") "-" join) (error) try "[symbol join] Quotation must contain only strings" ==) + (($4 () cat) (error) try "[symbol cat] Two quotations or two strings required" ==) + (($4 len) (error) try "[symbol len] Quotation or string required" ==) + (($1 $4 index) (error) try "[symbol index] Quotation or string required" ==) + ((("a" $2 "b") "-" join) (error) try "[symbol join] Quotation must contain only strings" ==) ;128 - ((("a" "b" "c") 0x1 join) (error) try "[symbol join] Quotation and string required" ==) - (("a" 0x1 split) (error) try "[symbol split] Two strings required" ==) + ((("a" "b" "c") $1 join) (error) try "[symbol join] Quotation and string required" ==) + (("a" $1 split) (error) try "[symbol split] Two strings required" ==) ((("aaa") "a" "b" sub) (error) try "[symbol sub] Three strings required" ==) - ((0x1 read) (error) try "[symbol read] String required" ==) + (($1 read) (error) try "[symbol read] String required" ==) ;132 (("invalid-file.txt" read) (error) try "[symbol read] Could not open file for reading: invalid-file.txt" ==) (("test" "invalid-file//" write) (error) try "[symbol write] Could not open file for writing: invalid-file//" ==) - (((0x2 0x4 "a") "test.txt" write) (error) try "[symbol write] Quotation must contain only integers" == "rm test.txt" exec drop) - ((0x2 "test.txt" write) (error) try "[symbol write] String or quotation of integers required" ==) + ((($2 $4 "a") "test.txt" write) (error) try "[symbol write] Quotation must contain only integers" == "rm test.txt" exec drop) + (($2 "test.txt" write) (error) try "[symbol write] String or quotation of integers required" ==) ;136 (("a" () write) (error) try "[symbol write] String required" ==) (("aaa" "invalid-file//" append) (error) try "[symbol append] Could not open file for appending: invalid-file//" ==) - (((0x2 "" 0x5) "test.txt" append) (error) try "[symbol append] Quotation must contain only integers" == "rm test.txt" exec drop) - ((0x2 "test.txt" append) (error) try "[symbol append] String or quotation of integers required" ==) + ((($2 "" $5) "test.txt" append) (error) try "[symbol append] Quotation must contain only integers" == "rm test.txt" exec drop) + (($2 "test.txt" append) (error) try "[symbol append] String or quotation of integers required" ==) ;140 - (("a" 0x1 append) (error) try "[symbol append] String required" ==) + (("a" $1 append) (error) try "[symbol append] String required" ==) (("error" exit) (error) try "[symbol exit] Integer required" ==) - ((0x1 exec) (error) try "[symbol exec] String required" ==) + (($1 exec) (error) try "[symbol exec] String required" ==) ((() run) (error) try "[symbol run] String required" ==) ;144 ((() () "" if) (error) try "[symbol if] Three quotations required" ==) ((() "" while) (error) try "[symbol while] Two quotations required" ==) - ((() 0x1 try) (error) try "[symbol try] Two quotations required" ==) - ((0x1 throw) (error) try "[symbol throw] String required" ==) + ((() $1 try) (error) try "[symbol try] Two quotations required" ==) + (($1 throw) (error) try "[symbol throw] String required" ==) ;148 - ((() 0x1 map) (error) try "[symbol map] Two quotations required" ==) + ((() $1 map) (error) try "[symbol map] Two quotations required" ==) ( ( - 0x1 0x2 "a" () "c" 0xf4 0xffffffff "d" "eeeee" "test \n aaa" - 0x1 0x2 "a" () "c" 0xf4 0xffffffff "d" "eeeee" "test \n aaa" - 0x1 0x2 "a" () "c" 0xf4 0xffffffff "d" "eeeee" "test \n aaa" - 0x1 0x2 "a" () "c" 0xf4 0xffffffff "d" "eeeee" "test \n aaa" - 0x1 0x2 "a" () "c" 0xf4 0xffffffff "d" "eeeee" "test \n aaa" - 0x1 0x2 "a" () "c" 0xf4 0xffffffff "d" "eeeee" "test \n aaa" - 0x1 0x2 "a" () "c" 0xf4 0xffffffff "d" "eeeee" "test \n aaa" - 0x1 0x2 "a" () "c" 0xf4 0xffffffff "d" "eeeee" "test \n aaa" - 0x1 0x2 "a" () "c" 0xf4 0xffffffff "d" "eeeee" "test \n aaa" - 0x1 0x2 "a" () "c" 0xf4 0xffffffff "d" "eeeee" "test \n aaa" - 0x1 0x2 "a" () "c" 0xf4 0xffffffff "d" "eeeee" "test \n aaa" - 0x1 0x2 "a" () "c" 0xf4 0xffffffff "d" "eeeee" "test \n aaa" - 0x1 0x2 "a" () "c" 0xf4 0xffffffff "d" "eeeee" "test \n aaa" + $1 $2 "a" () "c" $f4 $ffffffff "d" "eeeee" "test \n aaa" + $1 $2 "a" () "c" $f4 $ffffffff "d" "eeeee" "test \n aaa" + $1 $2 "a" () "c" $f4 $ffffffff "d" "eeeee" "test \n aaa" + $1 $2 "a" () "c" $f4 $ffffffff "d" "eeeee" "test \n aaa" + $1 $2 "a" () "c" $f4 $ffffffff "d" "eeeee" "test \n aaa" + $1 $2 "a" () "c" $f4 $ffffffff "d" "eeeee" "test \n aaa" + $1 $2 "a" () "c" $f4 $ffffffff "d" "eeeee" "test \n aaa" + $1 $2 "a" () "c" $f4 $ffffffff "d" "eeeee" "test \n aaa" + $1 $2 "a" () "c" $f4 $ffffffff "d" "eeeee" "test \n aaa" + $1 $2 "a" () "c" $f4 $ffffffff "d" "eeeee" "test \n aaa" + $1 $2 "a" () "c" $f4 $ffffffff "d" "eeeee" "test \n aaa" + $1 $2 "a" () "c" $f4 $ffffffff "d" "eeeee" "test \n aaa" + $1 $2 "a" () "c" $f4 $ffffffff "d" "eeeee" "test \n aaa" ) len "130" hex == ) ;150

@@ -295,22 +295,22 @@ ;------------------------------------------------------------------------;

; utils.hex tests ; ;------------------------------------------------------------------------; - (0x2 (0x1) (0x1) when 0x1 == swap drop) - (0x2 (0x0) (0x1) when 0x2 ==) + ($2 ($1) ($1) when $1 == swap drop) + ($2 ($0) ($1) when $2 ==) ("ccc" ("aaa") ("bbb") when "ccc" ==) - ((0x1) ("yes") ("no") unless "no" == swap drop) + (($1) ("yes") ("no") unless "no" == swap drop) ;154 - (() 0x1 push (0x1) ==) - ((0x1 0x2 0x3) pop (0x1 0x2) ==) - (0x0 (0x1 0x2 0x3) cons (0x0 0x1 0x2 0x3) ==) - ((0x1 0x2 0x3) uncons stack (0x1 (0x2 0x3)) == swap drop swap drop) + (() $1 push ($1) ==) + (($1 $2 $3) pop ($1 $2) ==) + ($0 ($1 $2 $3) cons ($0 $1 $2 $3) ==) + (($1 $2 $3) uncons stack ($1 ($2 $3)) == swap drop swap drop) ;158 - ((0x1 0x2 0x3) 0x0 swons (0x0 0x1 0x2 0x3) ==) - ((0x1 0x2 0x3) unswons stack ((0x2 0x3) 0x1) == swap drop swap drop) + (($1 $2 $3) $0 swons ($0 $1 $2 $3) ==) + (($1 $2 $3) unswons stack (($2 $3) $1) == swap drop swap drop) ("a" "b" over stack "_r" : drop drop drop ("a" "b" "a") _r == "_r" #) - (0x1 (0x2) dip stack "_r" : (0x2 0x1) _r == "_r" # drop drop) + ($1 ($2) dip stack "_r" : ($2 $1) _r == "_r" # drop drop) ;162 ("This is a test" "This" begins)

@@ -319,40 +319,40 @@ ("This is a test" "test" ends)

("This is a test" "Test" ends not) ;166 - ((0x1 0x2 0x3 0x5 0x6) "a" 0x3 ins (0x1 0x2 0x3 "a" 0x5 0x6) ==) - ((0x1 0x2 0x3 0x5 0x6) "a" 0x0 ins ("a" 0x1 0x2 0x3 0x5 0x6) ==) - ((0x1 0x2 0x3 0x5 0x6) "a" 0x4 ins (0x1 0x2 0x3 0x5 "a" 0x6) ==) - ((0x1 0x2 0x3 0x5 0x6) "a" 0x1 ins (0x1 "a" 0x2 0x3 0x5 0x6) ==) + (($1 $2 $3 $5 $6) "a" $3 ins ($1 $2 $3 "a" $5 $6) ==) + (($1 $2 $3 $5 $6) "a" $0 ins ("a" $1 $2 $3 $5 $6) ==) + (($1 $2 $3 $5 $6) "a" $4 ins ($1 $2 $3 $5 "a" $6) ==) + (($1 $2 $3 $5 $6) "a" $1 ins ($1 "a" $2 $3 $5 $6) ==) ;170 - ((0x4 0x3 "c" 0x2 "b" 0x1 "a") reverse ("a" 0x1 "b" 0x2 "c" 0x3 0x4) ==) - ((0x1) reverse (0x1) ==) - ((0x1 0x2) reverse (0x2 0x1) ==) + (($4 $3 "c" $2 "b" $1 "a") reverse ("a" $1 "b" $2 "c" $3 $4) ==) + (($1) reverse ($1) ==) + (($1 $2) reverse ($2 $1) ==) (() reverse () ==) ;174 - ((0x2 0x3 0x4) "a" 0x1 set (0x2 "a" 0x4) ==) - ((0x2 0x3 0x4) "a" 0x0 set ("a" 0x3 0x4) ==) - ((0x2 0x3 0x4) "a" 0x2 set (0x2 0x3 "a") ==) - ((0x2 0x3 0x4 0x5) "a" 0x3 set (0x2 0x3 0x4 "a") ==) + (($2 $3 $4) "a" $1 set ($2 "a" $4) ==) + (($2 $3 $4) "a" $0 set ("a" $3 $4) ==) + (($2 $3 $4) "a" $2 set ($2 $3 "a") ==) + (($2 $3 $4 $5) "a" $3 set ($2 $3 $4 "a") ==) ;178 - ((0x3 0x5 0x2 0x1 0x4) (>) sort (0x1 0x2 0x3 0x4 0x5) ==) - ((0x3 0x5 0x2 0x1 0x4) (<) sort (0x5 0x4 0x3 0x2 0x1) ==) + (($3 $5 $2 $1 $4) (>) sort ($1 $2 $3 $4 $5) ==) + (($3 $5 $2 $1 $4) (<) sort ($5 $4 $3 $2 $1) ==) (("a" "c" "b") (>) sort ("a" "b" "c") ==) (("a" "c" "b") (<) sort ("c" "b" "a") ==) ;182 - ((0x1 0x2 0x3 0x4) 0x2 rem (0x1 0x2 0x4) ==) - (((0x1 0x2 0x3 0x4) 0x4 rem) (error) try "[symbol rem] Index out of bounds" ==) - ((0x1 0x2 0x3 0x4) 0x0 rem (0x2 0x3 0x4) ==) - ((0x1 0x2 0x3 0x4) 0x3 rem (0x1 0x2 0x3) ==) + (($1 $2 $3 $4) $2 rem ($1 $2 $4) ==) + ((($1 $2 $3 $4) $4 rem) (error) try "[symbol rem] Index out of bounds" ==) + (($1 $2 $3 $4) $0 rem ($2 $3 $4) ==) + (($1 $2 $3 $4) $3 rem ($1 $2 $3) ==) ;186 ("This is a test! And another test" "test" "sample" gsub "This is a sample! And another sample" ==) ("This is a test! And another test" "sample" "test" gsub "This is a test! And another test" ==) - ((0x1 0x4 0x5) min 0x1 ==) - ((0x1 0x4 0x5) max 0x5 ==) + (($1 $4 $5) min $1 ==) + (($1 $4 $5) max $5 ==) ;190 ("${0}, ${1}!" ("hello" "world") fmt "hello, world!" ==)

@@ -361,26 +361,221 @@ ("This is a ${0}, and another ${0}, and yet another ${0}" ("test") fmt "This is a test, and another test, and yet another test" ==)

("${0}, ${2}, ${1}!" ("first" "third") fmt "first, ${2}, third!" ==) ;194 - (0x0 "_a" : (0x1 0x2 0x3) (_a + "_a" :) each _a 0x6 == "_a" #) - ((0x1 0x2 0x3 0x4) (0x2 % 0x0 ==) filter (0x2 0x4) ==) - ("Testing" 0x1 0x3 slice "est" ==) - (("a" 0xffffffff 0x0 slice) (error) try "[symbol slice] Index out of bounds" ==) + ($0 "_a" : ($1 $2 $3) (_a + "_a" :) each _a $6 == "_a" #) + (($1 $2 $3 $4) ($2 % $0 ==) filter ($2 $4) ==) + ("Testing" $1 $3 slice "est" ==) + (("a" $ffffffff $0 slice) (error) try "[symbol slice] Index out of bounds" ==) ;198 - (("a" 0x0 0x1 slice) (error) try "[symbol slice] Index out of bounds" ==) - ("a" 0x0 0x0 slice "a" ==) - ("" ("-" cat) 0x5 times "-----" ==) + (("a" $0 $1 slice) (error) try "[symbol slice] Index out of bounds" ==) + ("a" $0 $0 slice "a" ==) + ("" ("-" cat) $5 times "-----" ==) + ;201 + + ; --- Edge cases: empty string operations + ("" len $0 ==) + ("" "" cat "" ==) + ;203 + + ; --- Edge cases: negative integer arithmetic + ($ffffffff $1 + $0 ==) + ($0 $1 - $ffffffff ==) + ($ffffffff dec "-1" ==) + ($0 $1 - dec "-1" ==) + ;207 + + ; --- Empty quotation operations + (() len $0 ==) + (() () cat () ==) + (() () == $1 ==) + (() ($1) == $0 ==) + ;211 + + ; --- Nested try blocks: inner error caught, outer still works + ( + ( + ("error-inner" throw) (error) try + ) + (error) + try + "error-inner" == + ) + ; try with no error: body executes, catch block skipped + ( + $0 "try-result" : + ($1 "try-result" :) () try + try-result $1 == "try-result" # + ) + ;213 + + ; --- Symbol equality: comparing symbols to non-symbol types + ($1 $2 == $0 ==) + ("abc" "abc" == $1 ==) + ("abc" "xyz" == $0 ==) + ;216 + + ; --- Large quotation: create and measure + ( + ( + $1 $2 $3 $4 $5 $6 $7 $8 $9 $a + $1 $2 $3 $4 $5 $6 $7 $8 $9 $a + $1 $2 $3 $4 $5 $6 $7 $8 $9 $a + $1 $2 $3 $4 $5 $6 $7 $8 $9 $a + $1 $2 $3 $4 $5 $6 $7 $8 $9 $a + $1 $2 $3 $4 $5 $6 $7 $8 $9 $a + $1 $2 $3 $4 $5 $6 $7 $8 $9 $a + $1 $2 $3 $4 $5 $6 $7 $8 $9 $a + $1 $2 $3 $4 $5 $6 $7 $8 $9 $a + $1 $2 $3 $4 $5 $6 $7 $8 $9 $a + ) len "100" hex == + ) + ;217 + + ;------------------------------------------------------------------------; + ; Refactoring-safety tests: ownership, stack, error recovery, boundaries ; + ;------------------------------------------------------------------------; + + ; --- Group 1: Quotation ownership & deep-copy correctness + + ; A: map with quotation-typed results — copies action results into new quotation + (($1 $2) (') map (($1) ($2)) ==) + ; B: if then-branch — copies string items from thenBlock quotation + (("hello" len $3 >) ("yes") ("no") if "yes" ==) + ; C: if else-branch — copies string items from elseBlock quotation + (("hi" len $a >) ("yes") ("no") if "no" ==) + ; D: while building a quotation — copy cycle across iterations + ( + () "_wd_q" : + $0 "_wd_i" : + (_wd_i $5 <) + ( + _wd_q _wd_i ' cat "_wd_q" : + _wd_i $1 + "_wd_i" : + ) + while + _wd_q ($0 $1 $2 $3 $4) == "_wd_q" # "_wd_i" # + ) + ;221 + + ; E: nested while loops — copy+free cycle across nested iterations (3x3=9) + ( + $0 "_we_sum" : + $0 "_we_i" : + (_we_i $3 <) + ( + $0 "_we_j" : + (_we_j $3 <) + ( + _we_sum $1 + "_we_sum" : + _we_j $1 + "_we_j" : + ) + while + "_we_j" # + _we_i $1 + "_we_i" : + ) + while + _we_sum $9 == "_we_sum" # "_we_i" # + ) + ; F: user-defined operator (::) body — hex_push copies operator body items + ( + (' cat) "_wrapcat" :: + ($1) $2 _wrapcat ($1 $2) == "_wrapcat" # + ) + ; G: each on heterogeneous quotation (int + string + nested quotation) + ( + $0 "_eg_count" : + ("hello" $2a ($1 $2)) (drop _eg_count $1 + "_eg_count" :) each + _eg_count $3 == "_eg_count" # + ) + ; H: filter retaining quotation-typed elements + ((() ($1) ($1 $2)) (len $0 >) filter (($1) ($1 $2)) ==) + ;225 + + ; I: get returning a quotation from a nested structure — returned item is owned independently + ((($1 $2) ($3 $4)) $1 get ($3 $4) ==) + ; J: cat of nested quotations — result owns deep copies of both sources + ((($1) ($2)) (($3)) cat (($1) ($2) ($3)) ==) + ;227 + + ; --- Group 2: Stack manipulation with complex types + + ; K: swap two quotations — both independently owned after swap + (($1 $2) ($3 $4) swap ($1 $2) == swap ($3 $4) == and) + ; L: quote + dup + dequote — dup produces independent deep copy + ($1 ' dup . swap . and) + ; M: 4-level nested quotation dequote chain — recursive hex_copy_item + ((((($2a)))) . . . . $2a ==) + ; N: variable re-binding inside loop — : doesn't alias old and new values + ( + ($1) "_vr_v" : + $0 "_vr_i" : + (_vr_i $3 <) + ( + _vr_v _vr_i ' cat "_vr_v" : + _vr_i $1 + "_vr_i" : + ) + while + _vr_v ($1 $0 $1 $2) == "_vr_v" # "_vr_i" # + ) + ;231 + + ; --- Group 3: Error recovery paths + + ; O: try inside while, error thrown mid-loop, loop resumes after catch + ( + $0 "_to_i" : + (_to_i $3 <) + ( + ((_to_i $2 ==) ("found-2" throw) when) + (error drop) + try + _to_i $1 + "_to_i" : + ) + while + _to_i $3 == "_to_i" # + ) + ; P: throw with dynamically-built error message — string ownership through throw/error + (("error-" "42" cat throw) (error) try "error-42" ==) + ; Q: sequential try blocks have independent error state + ( + ("first" throw) (error) try "first" == + ("second" throw) (error) try "second" == + and + ) + ;234 + + ; --- Group 4: Boundary conditions and untested symbols + + ; R: timestamp returns a 2-element quotation of integers (seconds + microseconds) + (timestamp type "quotation" ==) + ; S: timestamp seconds element is non-negative + (timestamp $0 get $0 >=) + ; T: 256-element quotation at HEX_STACK_SIZE boundary + ( + () "_bq_q" : + $0 "_bq_i" : + (_bq_i $100 <) + ( + _bq_q _bq_i ' cat "_bq_q" : + _bq_i $1 + "_bq_i" : + ) + while + _bq_q len $100 == "_bq_q" # "_bq_i" # + ) + ; U: split where delimiter not found — returns single-element quotation + ("abc" "x" split ("abc") ==) + ;238 ) "TESTS" : ; --- Run Tests -0x0 "_c" : +$0 "_c" : (_c TESTS len <) ( TESTS _c get test - _c 0x1 + "_c" : + _c $1 + "_c" : ) while "_c" #

@@ -389,26 +584,26 @@ ; --- Report

"\nSuccessful Tests: " print SUCCESSES dec print "/" print SUCCESSES FAILURES + dec puts -(ERRORS len 0x0 >) +(ERRORS len $0 >) ( "Errors:" warn - 0x0 "_c" : + $0 "_c" : (_c ERRORS len <) ( ERRORS _c get warn - _c 0x1 + "_c" : + _c $1 + "_c" : ) while "_c" # - 0x1 "RESULT" : + $1 "RESULT" : ) when -(FAILS len 0x0 >) +(FAILS len $0 >) ( "Failed Tests: " FAILS ", " join cat warn - 0x1 "RESULT" : + $1 "RESULT" : ) when
M scripts/web.hexscripts/web.hex

@@ -2,8 +2,8 @@ #!/usr/bin/env hex

"./lib/utils.hbx" read "utils.hbx" ! -"2025" "meta-year" : -"0.6.0" "meta-release" : +"2026" "meta-year" : +"0.7.0" "meta-release" : "web/assets" "d-assets" : "web/templates" "d-templates" : "web/contents" "d-contents" :

@@ -11,7 +11,7 @@ "web/out" "d-out" :

"releases" "d-releases" : ; Get all files from a directory -("ls " swap cat run 0x1 get "\n" split) "ls" :: +("ls " swap cat run $1 get "\n" split) "ls" :: ; Get relevant files d-contents ls "contents" :

@@ -22,7 +22,7 @@

; Symbols to substitute with the corresponding links ("map" "debug" "split" "run" "get" "puts" ":" "::" "." "#" "==" "'" "swap" "dup" "while" "+" "*" "-" "each" "cat" "throw" "drop" "sub" -"symbols" "print" "read" "dec" "write" "append" "!" +"symbols" "print" "read" "dec" "write" "append" "!" "try" "error" "args" "<=" "if" "timestamp") "symbol-links" : ;; Syntax highlighting

@@ -33,7 +33,7 @@ "$;"

"$#|" "|#$" "$\"" - "$0x" + "$!" "$:" "$$" "%:"

@@ -47,7 +47,7 @@ "<span class=\"hex-comment\">;"

"<span class=\"hex-comment\">#|" "|#</span>" "<span class=\"hex-string\">\"" - "<span class=\"hex-integer\">0x" + "<span class=\"hex-integer\">$" "<span class=\"hex-symbol\">" "</span>" "<code>"

@@ -59,14 +59,14 @@

; Highlight syntax in text ( "t-text" : - 0x0 "t-count" : + $0 "t-count" : (t-count highlight-delimiters len <) ( highlight-delimiters t-count get "t-delimiter" : highlight-replacements t-count get "t-replacement" : ; Find delimiter in text and substitute it with the corresponding replacement t-text t-delimiter t-replacement gsub "t-text" : - t-count 0x1 + "t-count" : + t-count $1 + "t-count" : ) while ; Push highlighted text on the stack

@@ -102,6 +102,7 @@ "t-content" :

t-symbol "t-href" : ; Handle "special" symbols (t-symbol ":" ==)("store" "t-href" :) when + (t-symbol "==" ==)("equal" "t-href" :) when (t-symbol "<=" ==)("lessthanequal" "t-href" :) when (t-symbol "." ==)("i" "t-href" :) when (t-symbol "." ==)("i" "t-href" :) when

@@ -129,12 +130,12 @@ "" "releases-content" :

( releases len "_releases_count" : ; Reverse releases order - (_releases_count 0x0 >) + (_releases_count $0 >) ( - releases _releases_count 0x1 - get "id-release" : + releases _releases_count $1 - get "id-release" : ; Update releases-content d-releases "/" id-release cat cat read releases-content swap cat "releases-content" : - _releases_count 0x1 - "_releases_count" : + _releases_count $1 - "_releases_count" : ) while "_releases_count" #

@@ -146,14 +147,14 @@ (

"" "changelog-toc" : ; external releases len "_gct_count" : ; Reverse releases order - (_gct_count 0x0 >) + (_gct_count $0 >) ( - releases _gct_count 0x1 - get ".html" "" sub "id-release" : + releases _gct_count $1 - get ".html" "" sub "id-release" : changelog-toc ("<li><a href=\"#v" id-release "\">v" id-release "</a></li>\n") () map "" join cat "changelog-toc" : - _gct_count 0x1 - "_gct_count" : + _gct_count $1 - "_gct_count" : ) while "<ul>\n"

@@ -180,11 +181,11 @@ "changelog-toc" changelog-toc process-tag

"releases" releases-content process-tag "changelog-content" : -0x0 "_c" : +$0 "_c" : (_c symbol-links len <) ( changelog-content symbol-links _c get process-symbol "changelog-content" : - _c 0x1 + "_c" : + _c $1 + "_c" : ) while "_c" #

@@ -199,7 +200,7 @@ "lib/utils.html" read "lib_utils_docs" :

;;;;; Write web site contents "*** Generating hex web site..." puts -0x0 "_c" : +$0 "_c" : (_c contents len <) ( contents _c get "fn-content" :

@@ -217,11 +218,11 @@ ; Replace files

"@@@lib/utils.html@@@" lib_utils_docs sub "new-content" : ; Replace symbols with links - 0x0 "_i" : + $0 "_i" : (_i symbol-links len <) ( new-content symbol-links _i get process-symbol "new-content" : - _i 0x1 + "_i" : + _i $1 + "_i" : ) while "_i" #

@@ -239,7 +240,7 @@ )

if " - Writing: " dst-file cat puts new-content dst-file write - _c 0x1 + "_c" : + _c $1 + "_c" : ) while "_c" #

@@ -247,7 +248,7 @@

; Write assets ("sh -c \"mkdir -p " d-out "/assets\"") () map "" join exec -0x0 "_c" : +$0 "_c" : (_c assets len <) ( assets _c get "fn-asset" :

@@ -258,7 +259,7 @@ when

(d-assets fn-asset) () map "/" join "src-file" : " - Writing: " dst-file cat puts ("cp" src-file dst-file) () map " " join exec - _c 0x1 + "_c" : + _c $1 + "_c" : ) while "_c" #
M src/doc.csrc/doc.c

@@ -43,8 +43,8 @@ hex_set_doc(docs, "#", "s", "", "Deletes user symbol 's'.");

hex_set_doc(docs, "symbols", "", "q", "Pushes a quotation containing all registered symbols on the stack."); // Control flow - hex_set_doc(docs, "if", "q q q", "*", "If 'q1' is not 0x0, executes 'q2', else 'q3'."); - hex_set_doc(docs, "while", "q1 q2", "*", "While 'q1' is not 0x0, executes 'q2'."); + hex_set_doc(docs, "if", "q q q", "*", "If 'q1' is not $0, executes 'q2', else 'q3'."); + hex_set_doc(docs, "while", "q1 q2", "*", "While 'q1' is not $0, executes 'q2'."); hex_set_doc(docs, "error", "", "s", "Returns the last error message."); hex_set_doc(docs, "try", "q1 q2", "*", "If 'q1' fails, executes 'q2'."); hex_set_doc(docs, "throw", "s", "", "Throws error 's'.");

@@ -77,18 +77,18 @@ hex_set_doc(docs, "<<", "i1 12", "i", "Shifts 'i1' by 'i2' bytes to the left.");

hex_set_doc(docs, ">>", "i1 12", "i", "Shifts 'i1' by 'i2' bytes to the right."); // Comparison - hex_set_doc(docs, "==", "a1 a2", "i", "Returns 0x1 if 'a1' == 'a2', 0x0 otherwise."); - hex_set_doc(docs, "!=", "a1 a2", "i", "Returns 0x1 if 'a1' != 'a2', 0x0 otherwise."); - hex_set_doc(docs, ">", "a1 a2", "i", "Returns 0x1 if 'a1' > 'a2', 0x0 otherwise."); - hex_set_doc(docs, "<", "a1 a2", "i", "Returns 0x1 if 'a1' < 'a2', 0x0 otherwise."); - hex_set_doc(docs, ">=", "a1 a2", "i", "Returns 0x1 if 'a1' >= 'a2', 0x0 otherwise."); - hex_set_doc(docs, "<=", "a1 a2", "i", "Returns 0x1 if 'a1' <= 'a2', 0x0 otherwise."); + hex_set_doc(docs, "==", "a1 a2", "i", "Returns $1 if 'a1' == 'a2', $0 otherwise."); + hex_set_doc(docs, "!=", "a1 a2", "i", "Returns $1 if 'a1' != 'a2', $0 otherwise."); + hex_set_doc(docs, ">", "a1 a2", "i", "Returns $1 if 'a1' > 'a2', $0 otherwise."); + hex_set_doc(docs, "<", "a1 a2", "i", "Returns $1 if 'a1' < 'a2', $0 otherwise."); + hex_set_doc(docs, ">=", "a1 a2", "i", "Returns $1 if 'a1' >= 'a2', $0 otherwise."); + hex_set_doc(docs, "<=", "a1 a2", "i", "Returns $1 if 'a1' <= 'a2', $0 otherwise."); // Logical - hex_set_doc(docs, "and", "i1 i2", "i", "Returns 0x1 if both 'i1' and 'i2' are not 0x0."); - hex_set_doc(docs, "or", "i1 i2", "i", "Returns 0x1 if either 'i1' or 'i2' are not 0x0."); - hex_set_doc(docs, "not", "i", "i", "Returns 0x1 if 'i' is 0x0, 0x0 otherwise."); - hex_set_doc(docs, "xor", "i1 i2", "i", "Returns 0x1 if only one item is not 0x0."); + hex_set_doc(docs, "and", "i1 i2", "i", "Returns $1 if both 'i1' and 'i2' are not $0."); + hex_set_doc(docs, "or", "i1 i2", "i", "Returns $1 if either 'i1' or 'i2' are not $0."); + hex_set_doc(docs, "not", "i", "i", "Returns $1 if 'i' is $0, $0 otherwise."); + hex_set_doc(docs, "xor", "i1 i2", "i", "Returns $1 if only one item is not $0."); // Type hex_set_doc(docs, "int", "s", "i", "Converts a string to a hex integer.");
M src/hex.csrc/hex.c

@@ -23,7 +23,7 @@ #include <sys/wait.h>

#endif // Constants -#define HEX_VERSION "0.6.0" +#define HEX_VERSION "0.7.0" #define HEX_STDIN_BUFFER_SIZE 16384 #define HEX_INITIAL_REGISTRY_SIZE 512 #define HEX_REGISTRY_SIZE 4096

@@ -1469,8 +1469,8 @@ hex_set_doc(docs, "#", "s", "", "Deletes user symbol 's'.");

hex_set_doc(docs, "symbols", "", "q", "Pushes a quotation containing all registered symbols on the stack."); // Control flow - hex_set_doc(docs, "if", "q q q", "*", "If 'q1' is not 0x0, executes 'q2', else 'q3'."); - hex_set_doc(docs, "while", "q1 q2", "*", "While 'q1' is not 0x0, executes 'q2'."); + hex_set_doc(docs, "if", "q q q", "*", "If 'q1' is not $0, executes 'q2', else 'q3'."); + hex_set_doc(docs, "while", "q1 q2", "*", "While 'q1' is not $0, executes 'q2'."); hex_set_doc(docs, "error", "", "s", "Returns the last error message."); hex_set_doc(docs, "try", "q1 q2", "*", "If 'q1' fails, executes 'q2'."); hex_set_doc(docs, "throw", "s", "", "Throws error 's'.");

@@ -1503,18 +1503,18 @@ hex_set_doc(docs, "<<", "i1 12", "i", "Shifts 'i1' by 'i2' bytes to the left.");

hex_set_doc(docs, ">>", "i1 12", "i", "Shifts 'i1' by 'i2' bytes to the right."); // Comparison - hex_set_doc(docs, "==", "a1 a2", "i", "Returns 0x1 if 'a1' == 'a2', 0x0 otherwise."); - hex_set_doc(docs, "!=", "a1 a2", "i", "Returns 0x1 if 'a1' != 'a2', 0x0 otherwise."); - hex_set_doc(docs, ">", "a1 a2", "i", "Returns 0x1 if 'a1' > 'a2', 0x0 otherwise."); - hex_set_doc(docs, "<", "a1 a2", "i", "Returns 0x1 if 'a1' < 'a2', 0x0 otherwise."); - hex_set_doc(docs, ">=", "a1 a2", "i", "Returns 0x1 if 'a1' >= 'a2', 0x0 otherwise."); - hex_set_doc(docs, "<=", "a1 a2", "i", "Returns 0x1 if 'a1' <= 'a2', 0x0 otherwise."); + hex_set_doc(docs, "==", "a1 a2", "i", "Returns $1 if 'a1' == 'a2', $0 otherwise."); + hex_set_doc(docs, "!=", "a1 a2", "i", "Returns $1 if 'a1' != 'a2', $0 otherwise."); + hex_set_doc(docs, ">", "a1 a2", "i", "Returns $1 if 'a1' > 'a2', $0 otherwise."); + hex_set_doc(docs, "<", "a1 a2", "i", "Returns $1 if 'a1' < 'a2', $0 otherwise."); + hex_set_doc(docs, ">=", "a1 a2", "i", "Returns $1 if 'a1' >= 'a2', $0 otherwise."); + hex_set_doc(docs, "<=", "a1 a2", "i", "Returns $1 if 'a1' <= 'a2', $0 otherwise."); // Logical - hex_set_doc(docs, "and", "i1 i2", "i", "Returns 0x1 if both 'i1' and 'i2' are not 0x0."); - hex_set_doc(docs, "or", "i1 i2", "i", "Returns 0x1 if either 'i1' or 'i2' are not 0x0."); - hex_set_doc(docs, "not", "i", "i", "Returns 0x1 if 'i' is 0x0, 0x0 otherwise."); - hex_set_doc(docs, "xor", "i1 i2", "i", "Returns 0x1 if only one item is not 0x0."); + hex_set_doc(docs, "and", "i1 i2", "i", "Returns $1 if both 'i1' and 'i2' are not $0."); + hex_set_doc(docs, "or", "i1 i2", "i", "Returns $1 if either 'i1' or 'i2' are not $0."); + hex_set_doc(docs, "not", "i", "i", "Returns $1 if 'i' is $0, $0 otherwise."); + hex_set_doc(docs, "xor", "i1 i2", "i", "Returns $1 if only one item is not $0."); // Type hex_set_doc(docs, "int", "s", "i", "Converts a string to a hex integer.");

@@ -1731,12 +1731,12 @@ ptr++;

position->column++; token->type = HEX_TOKEN_STRING; } - else if (strncmp(ptr, "0x", 2) == 0 || strncmp(ptr, "0X", 2) == 0) + else if (*ptr == '$') { // Hexadecimal integer token const char *start = ptr; - ptr += 2; // Skip the "0x" prefix - position->column += 2; + ptr++; // Skip the "$" prefix + position->column++; while (isxdigit(*ptr)) { ptr++;

@@ -1807,8 +1807,10 @@ }

int32_t hex_parse_integer(const char *hex_str) { + // Skip the "$" prefix + const char *digits = hex_str + 1; // Parse the hexadecimal string as an unsigned 32-bit integer - uint32_t unsigned_value = (uint32_t)strtoul(hex_str, NULL, 16); + uint32_t unsigned_value = (uint32_t)strtoul(digits, NULL, 16); // Cast the unsigned value to a signed 32-bit integer return (int32_t)unsigned_value;

@@ -2583,7 +2585,7 @@ ////////////////////////////////////////

int hex_bytecode_integer(hex_context_t *ctx, uint8_t **bytecode, size_t *size, size_t *capacity, int32_t value) { - hex_debug(ctx, "PUSHIN[01]: 0x%x", value); + hex_debug(ctx, "PUSHIN[01]: $%x", value); // Check if we need to resize the buffer (size + int32_t size + opcode (1) + max encoded length (4)) if (*size + sizeof(int32_t) + 1 + 4 > *capacity) {

@@ -2640,6 +2642,7 @@ hex_error(ctx, "[add bytecode string] Memory allocation failed");

return 1; } hex_debug(ctx, "PUSHST[02]: \"%s\"", str); + free(str); // only needed for debug output size_t len = strlen(value); // Check if we need to resize the buffer (size + strlen + opcode (1) + max encoded length (4)) if (*size + len + 1 + 4 > *capacity)

@@ -2661,7 +2664,6 @@ {

if ((value[i] & 0x80) != 0) { hex_error(ctx, "[add bytecode string] Multi-byte characters are not supported - Cannot encode string: \"%s\"", value); - free(str); return 1; } }

@@ -2911,10 +2913,15 @@

*bytecode += length; *size -= length; - hex_debug(ctx, ">> PUSHIN[01]: 0x%x", value); - HEX_ALLOC(item) - item = hex_integer_item(ctx, value); + hex_debug(ctx, ">> PUSHIN[01]: $%x", value); + hex_item_t *item = hex_integer_item(ctx, value); + if (!item) + { + hex_error(ctx, "[interpret bytecode integer] Failed to allocate integer item"); + return 1; + } *result = *item; + free(item); return 0; }

@@ -2931,11 +2938,14 @@ {

hex_error(ctx, "[interpret bytecode string] Bytecode size too small to contain a string length"); return 1; } - length |= ((**bytecode & 0x7F) << shift); - shift += 7; + uint8_t b = **bytecode; (*bytecode)++; (*size)--; - } while (**bytecode & 0x80); + length |= ((b & 0x7F) << shift); + shift += 7; + if (!(b & 0x80)) + break; + } while (1); if (*size < length) {

@@ -2954,16 +2964,16 @@ value[length] = '\0';

*bytecode += length; *size -= length; - HEX_ALLOC(item); - item = hex_string_item(ctx, value); - *result = *item; - char *str = hex_process_string(value); - if (!str) + hex_item_t *item = hex_string_item(ctx, value); + free(value); // raw buffer no longer needed after string item is created + if (!item) { - hex_error(ctx, "[interpret bytecode string] Memory allocation failed"); + hex_error(ctx, "[interpret bytecode string] Failed to allocate string item"); return 1; } - hex_debug(ctx, ">> PUSHST[02]: \"%s\"", str); + *result = *item; + free(item); // free wrapper only; str_value is now owned by result + hex_debug(ctx, ">> PUSHST[02]: \"%s\"", result->data.str_value); return 0; }

@@ -2977,9 +2987,20 @@ hex_error(ctx, "[interpret bytecode native symbol] Invalid opcode for symbol");

return 1; } - HEX_ALLOC(item); + hex_item_t *item = calloc(1, sizeof(hex_item_t)); + if (!item) + { + hex_error(ctx, "[interpret bytecode native symbol] Memory allocation failed"); + return 1; + } item->type = HEX_TYPE_NATIVE_SYMBOL; - HEX_ALLOC(value); + hex_item_t *value = calloc(1, sizeof(hex_item_t)); + if (!value) + { + hex_error(ctx, "[interpret bytecode native symbol] Memory allocation failed"); + free(item); + return 1; + } hex_token_t *token = (hex_token_t *)malloc(sizeof(hex_token_t)); token->value = strdup(symbol); token->position = (hex_file_position_t *)malloc(sizeof(hex_file_position_t));

@@ -2991,29 +3012,33 @@ {

item->token = token; item->type = HEX_TYPE_NATIVE_SYMBOL; item->data.fn_value = value->data.fn_value; + hex_free_item(ctx, value); // free the registry copy (including its token) } else { hex_error(ctx, "(%d,%d) Unable to reference native symbol: %s (bytecode)", token->position->line, token->position->column, token->value); hex_free_token(token); + free(value); // just wrapper; get_symbol did not fill it + free(item); return 1; } hex_debug(ctx, ">> NATSYM[%02x]: %s", opcode, token->value); *result = *item; + free(item); // free wrapper only; token/fn_value now owned by result return 0; } int hex_interpret_bytecode_user_symbol(hex_context_t *ctx, uint8_t **bytecode, size_t *size, size_t position, const char *filename, hex_item_t *result) { - // Get the index of the symbol (one byte) - if (*size == 0) + // Get the 2-byte little-endian index of the symbol + if (*size < 2) { - hex_error(ctx, "[interpret bytecode user symbol] Bytecode size too small to contain a symbol length"); + hex_error(ctx, "[interpret bytecode user symbol] Bytecode size too small to contain a symbol index"); return 1; } - size_t index = **bytecode; - (*bytecode)++; - (*size)--; + uint16_t index = (uint16_t)((*bytecode)[0]) | ((uint16_t)((*bytecode)[1]) << 8); + (*bytecode) += 2; + (*size) -= 2; if (index >= ctx->symbol_table->count) {

@@ -3021,16 +3046,13 @@ hex_error(ctx, "[interpret bytecode user symbol] Symbol index out of bounds");

return 1; } char *value = hex_symboltable_get_value(ctx, index); - size_t length = strlen(value); if (!value) { hex_error(ctx, "[interpret bytecode user symbol] Memory allocation failed"); return 1; } - - *bytecode += 1; - *size -= 1; + size_t length = strlen(value); hex_token_t *token = (hex_token_t *)malloc(sizeof(hex_token_t));

@@ -3543,7 +3565,7 @@ {

switch (item.type) { case HEX_TYPE_INTEGER: - fprintf(stream, "0x%x", item.data.int_value); + fprintf(stream, "$%x", item.data.int_value); break; case HEX_TYPE_STRING: fprintf(stream, "%s", item.data.str_value);

@@ -3651,7 +3673,7 @@ {

switch (item->type) { case HEX_TYPE_INTEGER: - fprintf(stream, "0x%x", item->data.int_value); + fprintf(stream, "$%x", item->data.int_value); break; case HEX_TYPE_STRING:

@@ -4914,6 +4936,8 @@ return 0;

} if (a->type == HEX_TYPE_NATIVE_SYMBOL || a->type == HEX_TYPE_USER_SYMBOL) { + if (b->type != HEX_TYPE_NATIVE_SYMBOL && b->type != HEX_TYPE_USER_SYMBOL) + return 0; return (strcmp(a->token->value, b->token->value) == 0); } if (a->type != b->type)

@@ -5472,7 +5496,7 @@ HEX_FREE(ctx, index);

return 1; } int result = 0; - hex_item_t *copy = calloc(1, sizeof(hex_item_t)); + hex_item_t *copy = NULL; if (list->type == HEX_TYPE_QUOTATION) { if (index->type != HEX_TYPE_INTEGER)

@@ -6079,7 +6103,7 @@ for (size_t i = 0; i < (size_t)ctx->argc; i++)

{ quotation[i] = (hex_item_t *)calloc(1, sizeof(hex_item_t)); quotation[i]->type = HEX_TYPE_STRING; - quotation[i]->data.str_value = ctx->argv[i]; + quotation[i]->data.str_value = strdup(ctx->argv[i]); } if (hex_push_quotation(ctx, quotation, ctx->argc) != 0) {

@@ -6147,10 +6171,35 @@ HEX_FREE(ctx, command);

return 1; } - char output[8192] = ""; - char error[8192] = ""; + size_t output_len = 0, output_cap = 4096; + size_t error_len = 0, error_cap = 4096; + char *output = (char *)malloc(output_cap); + char *error_buf = (char *)malloc(error_cap); + if (!output || !error_buf) + { + free(output); + free(error_buf); + hex_error(ctx, "[symbol run] Memory allocation failed"); + HEX_FREE(ctx, command); + return 1; + } + output[0] = '\0'; + error_buf[0] = '\0'; int return_code = 0; +/* helper macro: append chunk to a dynamic buffer */ +#define RUN_APPEND(buf, buf_len, buf_cap, chunk, chunk_len) \ + do { \ + while ((buf_len) + (chunk_len) + 1 > (buf_cap)) \ + { \ + (buf_cap) *= 2; \ + char *_tmp = (char *)realloc((buf), (buf_cap)); \ + if (!_tmp) { free(buf); (buf) = NULL; break; } \ + (buf) = _tmp; \ + } \ + if (buf) { memcpy((buf) + (buf_len), (chunk), (chunk_len)); (buf_len) += (chunk_len); (buf)[(buf_len)] = '\0'; } \ + } while (0) + #ifdef _WIN32 // Windows implementation HANDLE hOutputRead, hOutputWrite;

@@ -6168,6 +6217,8 @@ // Create pipes for capturing stdout and stderr

if (!CreatePipe(&hOutputRead, &hOutputWrite, &sa, 0) || !CreatePipe(&hErrorRead, &hErrorWrite, &sa, 0)) { hex_error(ctx, "[symbol run] Failed to create pipes"); + free(output); + free(error_buf); HEX_FREE(ctx, command); return 1; }

@@ -6183,6 +6234,8 @@ // Create the child process

if (!CreateProcess(NULL, command->data.str_value, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { hex_error(ctx, "[symbol run] Failed to create process"); + free(output); + free(error_buf); HEX_FREE(ctx, command); return 1; }

@@ -6197,14 +6250,14 @@ char buffer[1024];

while (ReadFile(hOutputRead, buffer, sizeof(buffer) - 1, &bytesRead, NULL) && bytesRead > 0) { buffer[bytesRead] = '\0'; - strcat(output, buffer); + RUN_APPEND(output, output_len, output_cap, buffer, bytesRead); } // Read stderr while (ReadFile(hErrorRead, buffer, sizeof(buffer) - 1, &bytesRead, NULL) && bytesRead > 0) { buffer[bytesRead] = '\0'; - strcat(error, buffer); + RUN_APPEND(error_buf, error_len, error_cap, buffer, bytesRead); } // Wait for the child process to finish and get the return code

@@ -6224,6 +6277,8 @@ int stderr_pipe[2];

if (pipe(stdout_pipe) != 0 || pipe(stderr_pipe) != 0) { hex_error(ctx, "[symbol run] Failed to create pipes"); + free(output); + free(error_buf); HEX_FREE(ctx, command); return 1; }

@@ -6232,6 +6287,8 @@ pid_t pid = fork();

if (pid == -1) { hex_error(ctx, "[symbol run] Failed to fork process"); + free(output); + free(error_buf); HEX_FREE(ctx, command); return 1; }

@@ -6256,7 +6313,8 @@ FILE *stdout_fp = fdopen(stdout_pipe[0], "r");

char path[1035]; while (fgets(path, sizeof(path), stdout_fp) != NULL) { - strcat(output, path); + size_t chunk_len = strlen(path); + RUN_APPEND(output, output_len, output_cap, path, chunk_len); } fclose(stdout_fp);

@@ -6264,7 +6322,8 @@ // Read stderr

FILE *stderr_fp = fdopen(stderr_pipe[0], "r"); while (fgets(path, sizeof(path), stderr_fp) != NULL) { - strcat(error, path); + size_t chunk_len = strlen(path); + RUN_APPEND(error_buf, error_len, error_cap, path, chunk_len); } fclose(stderr_fp);

@@ -6274,6 +6333,16 @@ waitpid(pid, &status, 0);

return_code = WEXITSTATUS(status); } #endif +#undef RUN_APPEND + + if (!output || !error_buf) + { + free(output); + free(error_buf); + hex_error(ctx, "[symbol run] Memory allocation failed while reading output"); + HEX_FREE(ctx, command); + return 1; + } // Push the return code, output, and error as a quotation hex_item_t **quotation = (hex_item_t **)calloc(3, sizeof(hex_item_t *));

@@ -6283,12 +6352,13 @@ quotation[0]->data.int_value = return_code;

quotation[1] = (hex_item_t *)calloc(1, sizeof(hex_item_t)); quotation[1]->type = HEX_TYPE_STRING; - quotation[1]->data.str_value = strdup(output); + quotation[1]->data.str_value = output; // transfer ownership quotation[2] = (hex_item_t *)calloc(1, sizeof(hex_item_t)); quotation[2]->type = HEX_TYPE_STRING; - quotation[2]->data.str_value = strdup(error); + quotation[2]->data.str_value = error_buf; // transfer ownership + HEX_FREE(ctx, command); return hex_push_quotation(ctx, quotation, 3); }

@@ -6333,8 +6403,11 @@ else

{ for (size_t i = 0; i < condition->quotation_size; i++) { - if (hex_push(ctx, condition->data.quotation_value[i]) != 0) + hex_item_t *copy = hex_copy_item(ctx, condition->data.quotation_value[i]); + if (!copy || hex_push(ctx, copy) != 0) { + if (copy) + hex_free_item(ctx, copy); HEX_FREE(ctx, condition); HEX_FREE(ctx, thenBlock); HEX_FREE(ctx, elseBlock);

@@ -6342,13 +6415,15 @@ return 1;

} } HEX_POP(ctx, evalResult); - ; if (evalResult->type == HEX_TYPE_INTEGER && evalResult->data.int_value > 0) { for (size_t i = 0; i < thenBlock->quotation_size; i++) { - if (hex_push(ctx, thenBlock->data.quotation_value[i]) != 0) + hex_item_t *copy = hex_copy_item(ctx, thenBlock->data.quotation_value[i]); + if (!copy || hex_push(ctx, copy) != 0) { + if (copy) + hex_free_item(ctx, copy); HEX_FREE(ctx, condition); HEX_FREE(ctx, thenBlock); HEX_FREE(ctx, elseBlock);

@@ -6361,8 +6436,11 @@ else

{ for (size_t i = 0; i < elseBlock->quotation_size; i++) { - if (hex_push(ctx, elseBlock->data.quotation_value[i]) != 0) + hex_item_t *copy = hex_copy_item(ctx, elseBlock->data.quotation_value[i]); + if (!copy || hex_push(ctx, copy) != 0) { + if (copy) + hex_free_item(ctx, copy); HEX_FREE(ctx, condition); HEX_FREE(ctx, thenBlock); HEX_FREE(ctx, elseBlock);

@@ -6371,6 +6449,10 @@ return 1;

} } } + HEX_FREE(ctx, evalResult); + HEX_FREE(ctx, condition); + HEX_FREE(ctx, thenBlock); + HEX_FREE(ctx, elseBlock); } return 0; }

@@ -6454,7 +6536,9 @@ {

char *message = strdup(ctx->error); ctx->error[0] = '\0'; - return hex_push_string(ctx, message); + int result = hex_push_string(ctx, message); + free(message); + return result; } int hex_symbol_try(hex_context_t *ctx)

@@ -6489,9 +6573,13 @@

ctx->settings->errors_enabled = 0; for (size_t i = 0; i < try_block->quotation_size; i++) { - if (hex_push(ctx, try_block->data.quotation_value[i]) != 0) + hex_item_t *copy = hex_copy_item(ctx, try_block->data.quotation_value[i]); + if (!copy || hex_push(ctx, copy) != 0) { + if (copy) + hex_free_item(ctx, copy); ctx->settings->errors_enabled = 1; + // continue to allow catch block to handle the error } } ctx->settings->errors_enabled = 1;

@@ -6501,8 +6589,11 @@ {

hex_debug(ctx, "[symbol try] Handling error: %s", ctx->error); for (size_t i = 0; i < catch_block->quotation_size; i++) { - if (hex_push(ctx, catch_block->data.quotation_value[i]) != 0) + hex_item_t *copy = hex_copy_item(ctx, catch_block->data.quotation_value[i]); + if (!copy || hex_push(ctx, copy) != 0) { + if (copy) + hex_free_item(ctx, copy); HEX_FREE(ctx, catch_block); HEX_FREE(ctx, try_block); return 1;

@@ -6511,6 +6602,8 @@ }

} strncpy(ctx->error, prevError, sizeof(ctx->error)); + HEX_FREE(ctx, catch_block); + HEX_FREE(ctx, try_block); } return 0; }

@@ -7146,7 +7239,7 @@ " Symbols are evaluated only when they are pushed on the stack, therefore, symbols inside\n"

" quotations are not evaluated until the contents of the quotation are pushed on the stack.\n" " You can define your own symbols using the symbol ':' and execute a quotation with '.'.\n" "\n" - " Oh, and of course all integers are in hexadecimal format! ;)\n" + " Oh, and of course all integers are in hexadecimal format, prefixed with '$'! ;)\n" "\n" "SYMBOLS\n" " +---------+----------------------------+--------------------------------------------------------------------+\n"

@@ -7252,7 +7345,7 @@ if (argc > 1)

{ for (int i = 1; i < argc; i++) { - char *arg = strdup(argv[i]); + char *arg = argv[i]; if ((strcmp(arg, "-v") == 0 || strcmp(arg, "--version") == 0)) { printf("%s\n", HEX_VERSION);

@@ -7304,7 +7397,7 @@ else

{ if (!file) { - file = arg; + file = strdup(argv[i]); } // Ignore extra arguments }

@@ -7335,6 +7428,7 @@ {

hex_error(ctx, "[generate bytecode] Failed to generate bytecode"); free(fileContent); free(bytecode_file); + free(file); hex_destroy(ctx); return 1; }

@@ -7343,18 +7437,21 @@ {

free(fileContent); free(bytecode_file); free(bytecode); + free(file); hex_destroy(ctx); return 1; } free(fileContent); free(bytecode_file); free(bytecode); + free(file); hex_destroy(ctx); return 0; } else { int result = hex_interpret_file(ctx, file); + free(file); hex_destroy(ctx); return result; }
M src/hex.hsrc/hex.h

@@ -20,7 +20,7 @@ #include <sys/wait.h>

#endif // Constants -#define HEX_VERSION "0.6.0" +#define HEX_VERSION "0.7.0" #define HEX_STDIN_BUFFER_SIZE 16384 #define HEX_INITIAL_REGISTRY_SIZE 512 #define HEX_REGISTRY_SIZE 4096
M src/main.csrc/main.c

@@ -259,7 +259,7 @@ " Symbols are evaluated only when they are pushed on the stack, therefore, symbols inside\n"

" quotations are not evaluated until the contents of the quotation are pushed on the stack.\n" " You can define your own symbols using the symbol ':' and execute a quotation with '.'.\n" "\n" - " Oh, and of course all integers are in hexadecimal format! ;)\n" + " Oh, and of course all integers are in hexadecimal format, prefixed with '$'! ;)\n" "\n" "SYMBOLS\n" " +---------+----------------------------+--------------------------------------------------------------------+\n"

@@ -365,7 +365,7 @@ if (argc > 1)

{ for (int i = 1; i < argc; i++) { - char *arg = strdup(argv[i]); + char *arg = argv[i]; if ((strcmp(arg, "-v") == 0 || strcmp(arg, "--version") == 0)) { printf("%s\n", HEX_VERSION);

@@ -417,7 +417,7 @@ else

{ if (!file) { - file = arg; + file = strdup(argv[i]); } // Ignore extra arguments }

@@ -448,6 +448,7 @@ {

hex_error(ctx, "[generate bytecode] Failed to generate bytecode"); free(fileContent); free(bytecode_file); + free(file); hex_destroy(ctx); return 1; }

@@ -456,18 +457,21 @@ {

free(fileContent); free(bytecode_file); free(bytecode); + free(file); hex_destroy(ctx); return 1; } free(fileContent); free(bytecode_file); free(bytecode); + free(file); hex_destroy(ctx); return 0; } else { int result = hex_interpret_file(ctx, file); + free(file); hex_destroy(ctx); return result; }
M src/parser.csrc/parser.c

@@ -174,12 +174,12 @@ ptr++;

position->column++; token->type = HEX_TOKEN_STRING; } - else if (strncmp(ptr, "0x", 2) == 0 || strncmp(ptr, "0X", 2) == 0) + else if (*ptr == '$') { // Hexadecimal integer token const char *start = ptr; - ptr += 2; // Skip the "0x" prefix - position->column += 2; + ptr++; // Skip the "$" prefix + position->column++; while (isxdigit(*ptr)) { ptr++;

@@ -250,8 +250,10 @@ }

int32_t hex_parse_integer(const char *hex_str) { + // Skip the "$" prefix + const char *digits = hex_str + 1; // Parse the hexadecimal string as an unsigned 32-bit integer - uint32_t unsigned_value = (uint32_t)strtoul(hex_str, NULL, 16); + uint32_t unsigned_value = (uint32_t)strtoul(digits, NULL, 16); // Cast the unsigned value to a signed 32-bit integer return (int32_t)unsigned_value;
M src/symbols.csrc/symbols.c

@@ -906,6 +906,8 @@ return 0;

} if (a->type == HEX_TYPE_NATIVE_SYMBOL || a->type == HEX_TYPE_USER_SYMBOL) { + if (b->type != HEX_TYPE_NATIVE_SYMBOL && b->type != HEX_TYPE_USER_SYMBOL) + return 0; return (strcmp(a->token->value, b->token->value) == 0); } if (a->type != b->type)

@@ -1464,7 +1466,7 @@ HEX_FREE(ctx, index);

return 1; } int result = 0; - hex_item_t *copy = calloc(1, sizeof(hex_item_t)); + hex_item_t *copy = NULL; if (list->type == HEX_TYPE_QUOTATION) { if (index->type != HEX_TYPE_INTEGER)

@@ -2071,7 +2073,7 @@ for (size_t i = 0; i < (size_t)ctx->argc; i++)

{ quotation[i] = (hex_item_t *)calloc(1, sizeof(hex_item_t)); quotation[i]->type = HEX_TYPE_STRING; - quotation[i]->data.str_value = ctx->argv[i]; + quotation[i]->data.str_value = strdup(ctx->argv[i]); } if (hex_push_quotation(ctx, quotation, ctx->argc) != 0) {

@@ -2139,10 +2141,35 @@ HEX_FREE(ctx, command);

return 1; } - char output[8192] = ""; - char error[8192] = ""; + size_t output_len = 0, output_cap = 4096; + size_t error_len = 0, error_cap = 4096; + char *output = (char *)malloc(output_cap); + char *error_buf = (char *)malloc(error_cap); + if (!output || !error_buf) + { + free(output); + free(error_buf); + hex_error(ctx, "[symbol run] Memory allocation failed"); + HEX_FREE(ctx, command); + return 1; + } + output[0] = '\0'; + error_buf[0] = '\0'; int return_code = 0; +/* helper macro: append chunk to a dynamic buffer */ +#define RUN_APPEND(buf, buf_len, buf_cap, chunk, chunk_len) \ + do { \ + while ((buf_len) + (chunk_len) + 1 > (buf_cap)) \ + { \ + (buf_cap) *= 2; \ + char *_tmp = (char *)realloc((buf), (buf_cap)); \ + if (!_tmp) { free(buf); (buf) = NULL; break; } \ + (buf) = _tmp; \ + } \ + if (buf) { memcpy((buf) + (buf_len), (chunk), (chunk_len)); (buf_len) += (chunk_len); (buf)[(buf_len)] = '\0'; } \ + } while (0) + #ifdef _WIN32 // Windows implementation HANDLE hOutputRead, hOutputWrite;

@@ -2160,6 +2187,8 @@ // Create pipes for capturing stdout and stderr

if (!CreatePipe(&hOutputRead, &hOutputWrite, &sa, 0) || !CreatePipe(&hErrorRead, &hErrorWrite, &sa, 0)) { hex_error(ctx, "[symbol run] Failed to create pipes"); + free(output); + free(error_buf); HEX_FREE(ctx, command); return 1; }

@@ -2175,6 +2204,8 @@ // Create the child process

if (!CreateProcess(NULL, command->data.str_value, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { hex_error(ctx, "[symbol run] Failed to create process"); + free(output); + free(error_buf); HEX_FREE(ctx, command); return 1; }

@@ -2189,14 +2220,14 @@ char buffer[1024];

while (ReadFile(hOutputRead, buffer, sizeof(buffer) - 1, &bytesRead, NULL) && bytesRead > 0) { buffer[bytesRead] = '\0'; - strcat(output, buffer); + RUN_APPEND(output, output_len, output_cap, buffer, bytesRead); } // Read stderr while (ReadFile(hErrorRead, buffer, sizeof(buffer) - 1, &bytesRead, NULL) && bytesRead > 0) { buffer[bytesRead] = '\0'; - strcat(error, buffer); + RUN_APPEND(error_buf, error_len, error_cap, buffer, bytesRead); } // Wait for the child process to finish and get the return code

@@ -2216,6 +2247,8 @@ int stderr_pipe[2];

if (pipe(stdout_pipe) != 0 || pipe(stderr_pipe) != 0) { hex_error(ctx, "[symbol run] Failed to create pipes"); + free(output); + free(error_buf); HEX_FREE(ctx, command); return 1; }

@@ -2224,6 +2257,8 @@ pid_t pid = fork();

if (pid == -1) { hex_error(ctx, "[symbol run] Failed to fork process"); + free(output); + free(error_buf); HEX_FREE(ctx, command); return 1; }

@@ -2248,7 +2283,8 @@ FILE *stdout_fp = fdopen(stdout_pipe[0], "r");

char path[1035]; while (fgets(path, sizeof(path), stdout_fp) != NULL) { - strcat(output, path); + size_t chunk_len = strlen(path); + RUN_APPEND(output, output_len, output_cap, path, chunk_len); } fclose(stdout_fp);

@@ -2256,7 +2292,8 @@ // Read stderr

FILE *stderr_fp = fdopen(stderr_pipe[0], "r"); while (fgets(path, sizeof(path), stderr_fp) != NULL) { - strcat(error, path); + size_t chunk_len = strlen(path); + RUN_APPEND(error_buf, error_len, error_cap, path, chunk_len); } fclose(stderr_fp);

@@ -2266,6 +2303,16 @@ waitpid(pid, &status, 0);

return_code = WEXITSTATUS(status); } #endif +#undef RUN_APPEND + + if (!output || !error_buf) + { + free(output); + free(error_buf); + hex_error(ctx, "[symbol run] Memory allocation failed while reading output"); + HEX_FREE(ctx, command); + return 1; + } // Push the return code, output, and error as a quotation hex_item_t **quotation = (hex_item_t **)calloc(3, sizeof(hex_item_t *));

@@ -2275,12 +2322,13 @@ quotation[0]->data.int_value = return_code;

quotation[1] = (hex_item_t *)calloc(1, sizeof(hex_item_t)); quotation[1]->type = HEX_TYPE_STRING; - quotation[1]->data.str_value = strdup(output); + quotation[1]->data.str_value = output; // transfer ownership quotation[2] = (hex_item_t *)calloc(1, sizeof(hex_item_t)); quotation[2]->type = HEX_TYPE_STRING; - quotation[2]->data.str_value = strdup(error); + quotation[2]->data.str_value = error_buf; // transfer ownership + HEX_FREE(ctx, command); return hex_push_quotation(ctx, quotation, 3); }

@@ -2325,8 +2373,11 @@ else

{ for (size_t i = 0; i < condition->quotation_size; i++) { - if (hex_push(ctx, condition->data.quotation_value[i]) != 0) + hex_item_t *copy = hex_copy_item(ctx, condition->data.quotation_value[i]); + if (!copy || hex_push(ctx, copy) != 0) { + if (copy) + hex_free_item(ctx, copy); HEX_FREE(ctx, condition); HEX_FREE(ctx, thenBlock); HEX_FREE(ctx, elseBlock);

@@ -2334,13 +2385,15 @@ return 1;

} } HEX_POP(ctx, evalResult); - ; if (evalResult->type == HEX_TYPE_INTEGER && evalResult->data.int_value > 0) { for (size_t i = 0; i < thenBlock->quotation_size; i++) { - if (hex_push(ctx, thenBlock->data.quotation_value[i]) != 0) + hex_item_t *copy = hex_copy_item(ctx, thenBlock->data.quotation_value[i]); + if (!copy || hex_push(ctx, copy) != 0) { + if (copy) + hex_free_item(ctx, copy); HEX_FREE(ctx, condition); HEX_FREE(ctx, thenBlock); HEX_FREE(ctx, elseBlock);

@@ -2353,8 +2406,11 @@ else

{ for (size_t i = 0; i < elseBlock->quotation_size; i++) { - if (hex_push(ctx, elseBlock->data.quotation_value[i]) != 0) + hex_item_t *copy = hex_copy_item(ctx, elseBlock->data.quotation_value[i]); + if (!copy || hex_push(ctx, copy) != 0) { + if (copy) + hex_free_item(ctx, copy); HEX_FREE(ctx, condition); HEX_FREE(ctx, thenBlock); HEX_FREE(ctx, elseBlock);

@@ -2363,6 +2419,10 @@ return 1;

} } } + HEX_FREE(ctx, evalResult); + HEX_FREE(ctx, condition); + HEX_FREE(ctx, thenBlock); + HEX_FREE(ctx, elseBlock); } return 0; }

@@ -2446,7 +2506,9 @@ {

char *message = strdup(ctx->error); ctx->error[0] = '\0'; - return hex_push_string(ctx, message); + int result = hex_push_string(ctx, message); + free(message); + return result; } int hex_symbol_try(hex_context_t *ctx)

@@ -2481,9 +2543,13 @@

ctx->settings->errors_enabled = 0; for (size_t i = 0; i < try_block->quotation_size; i++) { - if (hex_push(ctx, try_block->data.quotation_value[i]) != 0) + hex_item_t *copy = hex_copy_item(ctx, try_block->data.quotation_value[i]); + if (!copy || hex_push(ctx, copy) != 0) { + if (copy) + hex_free_item(ctx, copy); ctx->settings->errors_enabled = 1; + // continue to allow catch block to handle the error } } ctx->settings->errors_enabled = 1;

@@ -2493,8 +2559,11 @@ {

hex_debug(ctx, "[symbol try] Handling error: %s", ctx->error); for (size_t i = 0; i < catch_block->quotation_size; i++) { - if (hex_push(ctx, catch_block->data.quotation_value[i]) != 0) + hex_item_t *copy = hex_copy_item(ctx, catch_block->data.quotation_value[i]); + if (!copy || hex_push(ctx, copy) != 0) { + if (copy) + hex_free_item(ctx, copy); HEX_FREE(ctx, catch_block); HEX_FREE(ctx, try_block); return 1;

@@ -2503,6 +2572,8 @@ }

} strncpy(ctx->error, prevError, sizeof(ctx->error)); + HEX_FREE(ctx, catch_block); + HEX_FREE(ctx, try_block); } return 0; }
M src/utils.csrc/utils.c

@@ -107,7 +107,7 @@ {

switch (item.type) { case HEX_TYPE_INTEGER: - fprintf(stream, "0x%x", item.data.int_value); + fprintf(stream, "$%x", item.data.int_value); break; case HEX_TYPE_STRING: fprintf(stream, "%s", item.data.str_value);

@@ -215,7 +215,7 @@ {

switch (item->type) { case HEX_TYPE_INTEGER: - fprintf(stream, "0x%x", item->data.int_value); + fprintf(stream, "$%x", item->data.int_value); break; case HEX_TYPE_STRING:
M src/vm.csrc/vm.c

@@ -8,7 +8,7 @@ ////////////////////////////////////////

int hex_bytecode_integer(hex_context_t *ctx, uint8_t **bytecode, size_t *size, size_t *capacity, int32_t value) { - hex_debug(ctx, "PUSHIN[01]: 0x%x", value); + hex_debug(ctx, "PUSHIN[01]: $%x", value); // Check if we need to resize the buffer (size + int32_t size + opcode (1) + max encoded length (4)) if (*size + sizeof(int32_t) + 1 + 4 > *capacity) {

@@ -65,6 +65,7 @@ hex_error(ctx, "[add bytecode string] Memory allocation failed");

return 1; } hex_debug(ctx, "PUSHST[02]: \"%s\"", str); + free(str); // only needed for debug output size_t len = strlen(value); // Check if we need to resize the buffer (size + strlen + opcode (1) + max encoded length (4)) if (*size + len + 1 + 4 > *capacity)

@@ -86,7 +87,6 @@ {

if ((value[i] & 0x80) != 0) { hex_error(ctx, "[add bytecode string] Multi-byte characters are not supported - Cannot encode string: \"%s\"", value); - free(str); return 1; } }

@@ -336,10 +336,15 @@

*bytecode += length; *size -= length; - hex_debug(ctx, ">> PUSHIN[01]: 0x%x", value); - HEX_ALLOC(item) - item = hex_integer_item(ctx, value); + hex_debug(ctx, ">> PUSHIN[01]: $%x", value); + hex_item_t *item = hex_integer_item(ctx, value); + if (!item) + { + hex_error(ctx, "[interpret bytecode integer] Failed to allocate integer item"); + return 1; + } *result = *item; + free(item); return 0; }

@@ -356,11 +361,14 @@ {

hex_error(ctx, "[interpret bytecode string] Bytecode size too small to contain a string length"); return 1; } - length |= ((**bytecode & 0x7F) << shift); - shift += 7; + uint8_t b = **bytecode; (*bytecode)++; (*size)--; - } while (**bytecode & 0x80); + length |= ((b & 0x7F) << shift); + shift += 7; + if (!(b & 0x80)) + break; + } while (1); if (*size < length) {

@@ -379,16 +387,16 @@ value[length] = '\0';

*bytecode += length; *size -= length; - HEX_ALLOC(item); - item = hex_string_item(ctx, value); - *result = *item; - char *str = hex_process_string(value); - if (!str) + hex_item_t *item = hex_string_item(ctx, value); + free(value); // raw buffer no longer needed after string item is created + if (!item) { - hex_error(ctx, "[interpret bytecode string] Memory allocation failed"); + hex_error(ctx, "[interpret bytecode string] Failed to allocate string item"); return 1; } - hex_debug(ctx, ">> PUSHST[02]: \"%s\"", str); + *result = *item; + free(item); // free wrapper only; str_value is now owned by result + hex_debug(ctx, ">> PUSHST[02]: \"%s\"", result->data.str_value); return 0; }

@@ -402,9 +410,20 @@ hex_error(ctx, "[interpret bytecode native symbol] Invalid opcode for symbol");

return 1; } - HEX_ALLOC(item); + hex_item_t *item = calloc(1, sizeof(hex_item_t)); + if (!item) + { + hex_error(ctx, "[interpret bytecode native symbol] Memory allocation failed"); + return 1; + } item->type = HEX_TYPE_NATIVE_SYMBOL; - HEX_ALLOC(value); + hex_item_t *value = calloc(1, sizeof(hex_item_t)); + if (!value) + { + hex_error(ctx, "[interpret bytecode native symbol] Memory allocation failed"); + free(item); + return 1; + } hex_token_t *token = (hex_token_t *)malloc(sizeof(hex_token_t)); token->value = strdup(symbol); token->position = (hex_file_position_t *)malloc(sizeof(hex_file_position_t));

@@ -416,29 +435,33 @@ {

item->token = token; item->type = HEX_TYPE_NATIVE_SYMBOL; item->data.fn_value = value->data.fn_value; + hex_free_item(ctx, value); // free the registry copy (including its token) } else { hex_error(ctx, "(%d,%d) Unable to reference native symbol: %s (bytecode)", token->position->line, token->position->column, token->value); hex_free_token(token); + free(value); // just wrapper; get_symbol did not fill it + free(item); return 1; } hex_debug(ctx, ">> NATSYM[%02x]: %s", opcode, token->value); *result = *item; + free(item); // free wrapper only; token/fn_value now owned by result return 0; } int hex_interpret_bytecode_user_symbol(hex_context_t *ctx, uint8_t **bytecode, size_t *size, size_t position, const char *filename, hex_item_t *result) { - // Get the index of the symbol (one byte) - if (*size == 0) + // Get the 2-byte little-endian index of the symbol + if (*size < 2) { - hex_error(ctx, "[interpret bytecode user symbol] Bytecode size too small to contain a symbol length"); + hex_error(ctx, "[interpret bytecode user symbol] Bytecode size too small to contain a symbol index"); return 1; } - size_t index = **bytecode; - (*bytecode)++; - (*size)--; + uint16_t index = (uint16_t)((*bytecode)[0]) | ((uint16_t)((*bytecode)[1]) << 8); + (*bytecode) += 2; + (*size) -= 2; if (index >= ctx->symbol_table->count) {

@@ -446,16 +469,13 @@ hex_error(ctx, "[interpret bytecode user symbol] Symbol index out of bounds");

return 1; } char *value = hex_symboltable_get_value(ctx, index); - size_t length = strlen(value); if (!value) { hex_error(ctx, "[interpret bytecode user symbol] Memory allocation failed"); return 1; } - - *bytecode += 1; - *size -= 1; + size_t length = strlen(value); hex_token_t *token = (hex_token_t *)malloc(sizeof(hex_token_t));
M web/contents/about.htmlweb/contents/about.html

@@ -40,7 +40,7 @@ <pre><code> _*_ _

/ \hex\* *\_/_/_/ *</code></pre>It also features four <em>stars</em>, representing - the the 64 native symbols provided by the language (four times 16, so <code>0x40</code> in hexadecimal format). + the the 64 native symbols provided by the language (four times 16, so <code>$40</code> in hexadecimal format). They also hint at the magic of the language itself, and the dual meaning of the word <em>hex</em> (as in <em>hexadecimal</em> and <em>spell</em>). </p>
M web/contents/get.htmlweb/contents/get.html

@@ -91,7 +91,7 @@ <p>If you do not specify any option or argument, a simple <abbr title="Read-Eval-Print-Loop">REPL</abbr> will be

started.</p> <p>Alternatively, you can also pipe input from standard input: <p> - <pre><code>echo "0x2 0x2 + puts" | hex</code></pre> + <pre><code>echo "$2 $2 + puts" | hex</code></pre> <h3>Syntax Highlighting</h3> <p>If you use the Vim editor, you can use <a href="https://github.com/h3rald/hex/blob/master/hex.vim"
M web/contents/home.htmlweb/contents/home.html

@@ -29,14 +29,14 @@ <h3>Example</h3>

<p> The following code executes the <em>ls</em> command, retrieves the files returned in standard output, and stores them inside a quotation that is then printed:</p> - <pre><code>$"ls"$$ $:run$$ $0x1$$ $:get$$ $"&bsol;n"$$ $:split$$ $:puts$$</code></pre> + <pre><code>$"ls"$$ $:run$$ $!1$$ $:get$$ $"&bsol;n"$$ $:split$$ $:puts$$</code></pre> <p>Note that <strong>no variable is used in the code above</strong>! Let's break it down:</p> <ul> <li>The string <code>"ls"</code> is pushed on the stack.</li> <li>The {{sym-run}} symbol is executed, which runs the command <code>ls</code> and pushes quotation on the stack containing three elements: the return code of the program, its standard output, and its standard error.</li> - <li>The integer <code>0x1</code> is pushed on the stack.</li> + <li>The integer <code>$1</code> is pushed on the stack.</li> <li>The {{sym-get}} symbol is executed, which retrieves the second element of the quotation (the standard output).</li> <li>The string <code>"&bsol;n"</code> is pushed on the stack.</li>
M web/contents/learn.htmlweb/contents/learn.html

@@ -37,13 +37,13 @@ <a href="/play">try it</a>!) and press ENTER, you'll get something like this:

</p> <pre><code>ERROR: Invalid symbol: 27</code></pre> <p>Right, because <em>hex</em> only understands integers in hexadecimal format. </p> - <p>Now, if you type <code>$0x1b$$</code> instead... well, at least it doesn't complain, right? You can try - entering <code>$0xffffffed$$</code> now (that's -19 in hexadecimal format using <a + <p>Now, if you type <code>$!1b$$</code> instead... well, at least it doesn't complain, right? You can try + entering <code>$!ffffffed$$</code> now (that's -19 in hexadecimal format using <a href="https://en.wikipedia.org/wiki/Two%27s_complement">two's complement</a>), and that works too.</p> <p>Now what just happened is that you pushed two values (integers, even) on <em>The Stack</em> (more on this later). Since you have two numbers on The Stack already, you may as well enter {{sym-+}} to add them up, and that gives you: </p> - <pre><code>0x8</code></pre> + <pre><code>$8</code></pre> <p>Jolly good. </p> <p>Now... <code>$:+$$</code> is actually a <strong>symbol</strong>; and symbols... well, they do tricks, those symbols, every time you try to push them on the stack. </p>

@@ -58,7 +58,7 @@ <p>Next... let's see. You can type {{sym-:}} (which is another symbol), and... nothing happens! </p>

<p>Or better, nothing gets pushed back on The Stack. <code>$::$$</code> is a greedy, selfish symbol that just eats a value (any literal) and a string, and doesn't put anything back on The Stack.</p> <p>Now type <code>$:eight$$</code> (with no quotes) and press ENTER:</p> - <pre><code>0x8</code></pre> + <pre><code>$8</code></pre> <p>Aha! It turns out that our <code>$::$$</code> friend works for <em>The Registry</em>. The Registry likes to keep things for itself. Values don't just get pushed and popped from The Registry, no sir! It ain't like The Stack. Once you are in, you are in, and you can't get out that easily (unless you are <a

@@ -68,7 +68,7 @@ <p>What's missing? Let's see, we talked about <em>integers</em>, <em>strings</em>, and even a little bit

about <em>symbols</em>... Ah! Right: <strong>quotations</strong>, of course!</p> <p>A quotation is a fancy name for an array, or a list. In <em>hex</em> quotations have no internal separators between items, and are delimited by <s>square</s> round brackets, like this:</p> - <pre><code>($0x1$$ $"two"$$ $:three$$ $:!$$)</code></pre> + <pre><code>($!1$$ $"two"$$ $:three$$ $:!$$)</code></pre> <p>Oh my! You really can put <em>anything</em> in quotations, right? Assuming that it's a valid literal, a known native symbol (like {{sym-!}}), or a syntactically-valid user-defined symbol (even if it doesn't exist, like <code>$:three$$</code>). You can even nest a quotation in another quotation, and another, and another...

@@ -81,16 +81,16 @@ <p>We had to mention The Stack earlier, it was unavoidable. See, The Stack is where the magic happens! But what is

it, you ask? Well, let's try a simple example and try to use hex to subtract 3 from 5, and take it reeeally slow.</p> <p>Fiiiirst we start a hex REPL.</p> - <p>Then, we enter <code>$0x5$$</code> and press <code>ENTER</code>. <code>$0x5</code> gets <em>pushed on The + <p>Then, we enter <code>$!5$$</code> and press <code>ENTER</code>. <code>$$5</code> gets <em>pushed on The Stack</em>, like this:</p> <pre><code> +-----------+ - | 0x5 | + | $5 | +-----------+</code></pre> - <p>Then, we enter <code>$0x3$$</code> on The Stack. Now there are two items on The Stack, like this:</p> + <p>Then, we enter <code>$!3$$</code> on The Stack. Now there are two items on The Stack, like this:</p> <pre><code> +-----------+ - | 0x3 | + | $3 | +-----------+ - | 0x5 | + | $5 | +-----------+</code></pre> <p>Great, and finally, we are going to push the symbol {{sym--}} on the stack, because that's how postfix notation (a.k.a. <a href="https://en.wikipedia.org/wiki/Reverse_Polish_notation">Reverse Polish Notation</a>) works:

@@ -100,23 +100,23 @@ <p>Anyhow, what happens to The Stack now? Waait... wait...</p>

<pre><code> * * - * +-----------+ - | 0x3 | + | $3 | +-----------+ - | 0x5 | + | $5 | +-----------+</code></pre> <p>...magic! Real quick, <code>$:-$$</code> takes two items from The Stack, performs the subtraction, aaaand pushes the result back on The Stack, that now looks like this:</p> <pre><code> +-----------+ - | 0x2 | + | $2 | +-----------+</code></pre> <p>Symbols ain't that bad after all. And yes, The Stack is AWESOME! Did you know that if you use postfix notation you will NEVER ever need to use parenthesis when performing math operations to tweak operator preference? No? Let's try it. Let's calculate <code>(3 + 2) * 7</code>:</p> <p>First, the sum, right? so:</p> - <pre><code>$0x3$$ $0x2$$ $:+$$</code></pre> + <pre><code>$!3$$ $!2$$ $:+$$</code></pre> <p>...then we simply add the multiplication, and so we have it:</p> - <pre><code>$0x3$$ $0x2$$ $:+$$ $0x7$$ $:*$$</code></pre> + <pre><code>$!3$$ $!2$$ $:+$$ $!7$$ $:*$$</code></pre> <p>If we take this further, you can use The Stack as an accumulator for your program state, and <em>never, ever use a variable</em>.</p> <p>Whaaaaaaat?</p>

@@ -137,13 +137,13 @@ implement and use. You have to know it, and you have to be very careful with it.</p>

<p>Now... to add a new symbol to The Registry, you use the {{sym-:}} symbol. That can also be used to overwrite existing symbols with new values, but not native symbols.</p> <p>Say we want to teach hex some Roman numerals... we could do this:</p> - <pre><code> $0x1$$ $"I"$$ $::$$ - $0x2$$ $"II"$$ $::$$ - $0x3$$ $"III"$$ $::$$ - $0x4$$ $"IV"$$ $::$$ + <pre><code> $!1$$ $"I"$$ $::$$ + $!2$$ $"II"$$ $::$$ + $!3$$ $"III"$$ $::$$ + $!4$$ $"IV"$$ $::$$ $; ...$$</code></pre> <p>Then, you could use them like ordinary symbols:</p> - <pre><code>$:I$$ $:IV$$ $:+$$ $; Pushes 0x5 on the stack</code></pre> + <pre><code>$:I$$ $:IV$$ $:+$$ $; Pushes $5 on the stack</code></pre> <p>If you don't need a symbol anymore, you can use the {{sym-#}} symbol to free it from The Registry. See? Simple. </p> <p>Of course The Registry is smart enough to stop you from freeing native symbols!</p>

@@ -158,19 +158,19 @@ <p>Alright, let's do something actually useful. I know: let's implement a new operator to implement the <em>factorial</em> of an integer! You never know when you'll need a factorial these days.</p>

<p>Here goes:</p> <pre><code>( $"_fact_n"$$ $::$$ - ($:_fact_n$$ $0x0$$ $:&lt;=$$) - ($0x1$$) - ($:_fact_n$$ $:_fact_n$$ $0x1$$ $:-$$ $:fact$$ $:*$$) + ($:_fact_n$$ $!0$$ $:&lt;=$$) + ($!1$$) + ($:_fact_n$$ $:_fact_n$$ $!1$$ $:-$$ $:fact$$ $:*$$) $:if$$ $"_fact_n"$$ $:#$$ ) $"fact"$$ $:::$$ -$0x5$$ $:fact$$ $:dec$$ $:puts$$ $; Prints 120 $$ +$!5$$ $:fact$$ $:dec$$ $:puts$$ $; Prints 120 $$ </code></pre> <p>Woah! That was a mouthful, wasn't it? Before breaking it down, look at the very end of the program: see that {{sym-::}}? That's the symbol to store <em>operator</em> symbols in The Registry. Operator symbols are defined using a quotation, but unlike ordinary quotations (stored using {{sym-:}}), they will be <em>immediately dequoted</em> when pushed on the stack. In other words, our $:fact$$ operator symbols will behave exactly like one of the built-in native symbol.</p> <p>Let's see what is happening inside the quotation:</p> <ul> -<li>First, we are storing a symbol $:_fact_n$$ in the registry. Wait, but there's no value? Correct, the value will be provided when the symbol is used, so like $0x5$$ $:fact$$. It's like saying that we are <em>expecting</em> a value on the stack (and here we are assuming it's an integer, but that's ok for this example).</li> +<li>First, we are storing a symbol $:_fact_n$$ in the registry. Wait, but there's no value? Correct, the value will be provided when the symbol is used, so like $!5$$ $:fact$$. It's like saying that we are <em>expecting</em> a value on the stack (and here we are assuming it's an integer, but that's ok for this example).</li> <li>Then three (!) quotations and the symbol {{sym-if}} will be pushed on the stack. Yep, you got that right: that's a good old if clause. The first quotation is the condition to be checked, then the <em>then</em> quotation, and finally the <em>else</em> quotation. Note how we can recursively call the $:fact$$ operator that we are just defining... mind-blowing, I am sure.</li> <li>Finally, we need to free the temporary symbol $:_fact_n$$ using {{sym-#}}. It is always good practice to do so, otherwise The Registry will be littered with stale symbols nobody uses anymore... quite a sore sight, believe me! Plus they take up precious memory.</li> </ul>
M web/contents/play.htmlweb/contents/play.html

@@ -2,10 +2,10 @@ <article>

<h2>Playground</h2> <p>You can use the REPL below to try out hex directly in your browser. This is the <em>actual</em> hex REPL, compiled to <a href="https://webassembly.org">WebAssembly</a>.</p> - <p>For example, try entering the following code, which pushes the quotation <code>($0x1$$ $0x8$$ $0x1b$$)</code> on + <p>For example, try entering the following code, which pushes the quotation <code>($!1$$ $!8$$ $!1b$$)</code> on the stack: </p> - <pre><code>($0x1$$ $0x2$$ $0x3$$) ($:dup$$ $:dup$$ $:*$$ $:*$$) $:map$$</code></pre> + <pre><code>($!1$$ $!2$$ $!3$$) ($:dup$$ $:dup$$ $:*$$ $:*$$) $:map$$</code></pre> <section> <section></section> <aside id="input">
M web/contents/spec.htmlweb/contents/spec.html

@@ -79,12 +79,12 @@ <p>hex programs are written as sequences of whitespace-separated tokens. Tokens can be literals, symbols, or

comments.</p> <p>This is an example of a simple hex program:</p> <pre><code> $; Filters a quotation to keep only the even numbers$$ - ($0x2$$ $0x3$$ $0x4$$ $0x5$$ $0x6$$) ($0x2$$ $:%$$ $0x0$$ $:==$$) $:filter$$</code></pre> + ($!2$$ $!3$$ $!4$$ $!5$$ $!6$$) ($!2$$ $:%$$ $!0$$ $:==$$) $:filter$$</code></pre> <p>This example includes:</p> <ul> <li>One single-line comment: <code>$; Filters a quotation to keep only the even numbers$$</code></li> - <li>Two quotations: <code>($0x2$$ $0x3$$ $0x4$$ $0x5$$ $0x6$$)</code> and - <code>($0x2$$ $:%$$ $0x0$$ $:==$$)</code> + <li>Two quotations: <code>($!2$$ $!3$$ $!4$$ $!5$$ $!6$$)</code> and + <code>($!2$$ $:%$$ $!0$$ $:==$$)</code> </li> <li>Three symbols: <code>$:%$$</code>, <code>$:==$$</code>, and <code>$:filter$$</code></li> </ul>

@@ -98,7 +98,7 @@ <p>Single-line comments start with a semicolon (<code>;</code>) and continue until the end of the line. Everything

after the semicolon is ignored.</p> <p>Example:</p> <pre><code> $; This is a single-line comment$$ - $0x2$$ $0x3$$ $:+$$ $; This adds 0x2 and 0x3$</code></pre> + $!2$$ $!3$$ $:+$$ $; This adds $2 and $3$</code></pre> <h5 id="multi-line-comments">Multi-line Comments<a href="#top"></a></h5> <p>Multi-line comments start with <code>#|</code> and end with <code>|#</code>. Everything between these markers is

@@ -108,9 +108,9 @@ <pre><code> $#|

This is a multi-line comment It can span multiple lines |#$ - $0x2$$ $0x3$$ $:+$$ $#| This adds 0x2 and 0x3 |#$</code></pre> + $!2$$ $!3$$ $:+$$ $#| This adds $2 and $3 |#$</code></pre> <h4 id="integer-literals">Integer Literals<a href="#top"></a></h4> - <p>Integer literals in hex are always written in hexadecimal form, prefixed with <code>0x</code>. They can contain + <p>Integer literals in hex are always written in hexadecimal form, prefixed with <code>$</code>. They can contain up to 8 hexadecimal digits, representing 32-bit integers. Hexadecimal digits include the numbers <code>0-9</code> and the letters <code>>a-f</code> (or <code>A-F</code>), which correspond to the decimal values 10-15.

@@ -120,10 +120,10 @@ href="https://en.wikipedia.org/wiki/Two%27s_complement" target="_blank">two's complement</a> representation.

For more information on two's complement, see .</p> <p>Examples:</p> <ul> - <li><code>$0x1$$</code> represents the decimal value 1.</li> - <li><code>$0xa$$</code> represents the decimal value 10.</li> - <li><code>$0x1f$$</code> represents the decimal value 31.</li> - <li><code>$0xffffffff$$</code> represents the decimal value -1 (in two's complement).</li> + <li><code>$!1$$</code> represents the decimal value 1.</li> + <li><code>$!a$$</code> represents the decimal value 10.</li> + <li><code>$!1f$$</code> represents the decimal value 31.</li> + <li><code>$!ffffffff$$</code> represents the decimal value -1 (in two's complement).</li> </ul> <p>Integers are case-insensitive; typically, lowercase letters are preferred but not mandatory.</p> <h4 id="string-literals">String Literals<a href="#top"></a></h4>

@@ -148,8 +148,8 @@ They can contain integers, strings, symbols, and

even other quotations, allowing for nested structures.</p> <p>Examples:</p> <ul> - <li><code>($0x1$$ $0x2$$ $0x3$$)</code> - A quotation containing three integer literals.</li> - <li><code>($0x1$$ $"hello"$$ ($0x2$$ $0x3$$))</code> - A nested quotation containing an integer, a string, and + <li><code>($!1$$ $!2$$ $!3$$)</code> - A quotation containing three integer literals.</li> + <li><code>($!1$$ $"hello"$$ ($!2$$ $!3$$))</code> - A nested quotation containing an integer, a string, and another quotation.</li> </ul>

@@ -157,7 +157,7 @@ <p>Unlike string literals, quotations can span multiple lines, making them suitable for representing complex data

structures and control flow mechanisms.</p> <h4 id="symbol-identifiers">Symbol Identifiers<a href="#top"></a></h4> <p>Symbol identifiers in hex are used to represent built-in native symbols and user-defined symbols.</p> - <p>There are 0x40 (64) <a href="#native-symbols">native symbols</a> in hex, and some of them contain special + <p>There are $40 (64) <a href="#native-symbols">native symbols</a> in hex, and some of them contain special characters like <code>==</code> or <code>.</code></p> <p>Instead, user-defined symbols:</p> <ul>

@@ -180,19 +180,19 @@ <p>Integers in hex are 32-bit signed values represented in hexadecimal form. They can be positive or negative (using

two's complement), and range from <code>-2,147,483,647</code> (<code>-2<sup>31</sup></code>) and <code>2,147,483,647</code> (<code>2<sup>31</sup> - 1</code>) </p> - <p>Integers are written using the prefix <code>$0x$$</code> followed by up to 8 hexadecimal digits.</p> - <p>hex integers are case-insensitive, meaning that <code>$0x1f$$</code> and - <code><span class="hex-integer">0X1F</span></code> are equivalent + <p>Integers are written using the prefix <code>$</code> followed by up to 8 hexadecimal digits.</p> + <p>hex integers are case-insensitive, meaning that <code>$!1f$$</code> and + <code><span class="hex-integer">$1F</span></code> are equivalent (however, lowercase letters are preferred). </p> computations.</p> - <p>Because hex has no boolean data type, $0x0$$ is assumed to be false, and any other integer value is assumed to be + <p>Because hex has no boolean data type, $!0$$ is assumed to be false, and any other integer value is assumed to be true.</p> <p>Examples:</p> <ul> - <li><code>$0x1$$</code> &mdash; Represents the decimal value 1.</li> - <li><code>$0xffffffff$$</code> &mdash; Represents the decimal value -1.</li> - <li><code>$0x10$$</code> &mdash; Represents the decimal value 16.</li> + <li><code>$!1$$</code> &mdash; Represents the decimal value 1.</li> + <li><code>$!ffffffff$$</code> &mdash; Represents the decimal value -1.</li> + <li><code>$!10$$</code> &mdash; Represents the decimal value 16.</li> </ul> <h4 id="strings">Strings<a href="#top"></a></h4> <p>Strings in hex are sequences of characters delimited by double quotes (<code>"</code>). They can contain any

@@ -215,17 +215,17 @@ this is a fundamental property of hex and other concatenative programming languages, because it means that

quotation effectively acts as code blocks, holding code that can be executed later on using appropriate dequoting symbols.</p> <p>Consider the following example:</p> - <pre><code> $0x0$$ $"t-count"$$ $::$$ - ($:t-count$$ $0xa$$ $:&lt;$$) + <pre><code> $!0$$ $"t-count"$$ $::$$ + ($:t-count$$ $!a$$ $:&lt;$$) ( $:t-count$$ $:puts$$ - $:t-count$$ $0x1$$ $:+$$ $"t-count"$$ $::$$ + $:t-count$$ $!1$$ $:+$$ $"t-count"$$ $::$$ ) $:while$$ $"t-count"$$ $:#$$</code></pre> <p>This example defines a symbol <code>$:t-count$$</code> that counts from 0 to 9 and prints each number to the standard - output. The quotation <code>($:t-count$$ $0xa$$ $:&lt;$$)</code> is used to check if the count is less than 10, + output. The quotation <code>($:t-count$$ $!a$$ $:&lt;$$)</code> is used to check if the count is less than 10, and the <code>$:while$$</code> symbol repeats the process until the condition is no longer met. </p>

@@ -235,7 +235,7 @@ <h4 id="symbols">Symbols<a href="#top"></a></h4>

<p>In hex there native symbols and user-defined symbols. Native symbols are built-in functions that perform specific operations, while user-defined symbols are created by the user to store values or define custom behavior.</p> - <p>hex provides 64 ($0x40$$) native symbols that cover a wide range of functionality, including arithmetic + <p>hex provides 64 ($!40$$) native symbols that cover a wide range of functionality, including arithmetic operations, control flow, I/O operations, file manipulation, and stack manipulation.</p> <p>You can think of symbols as both functions that manipulate the <a href="#stack">stack</a>, or variables that can be used to store literal values.</p>

@@ -262,9 +262,9 @@ quotations. When a literal is encountered in a hex program, it is pushed onto the stack for further processing.

</p> <p>Examples:</p> <ul> - <li><code>$0x1$$</code> &mdash; Pushes the integer 1 onto the stack.</li> + <li><code>$!1$$</code> &mdash; Pushes the integer 1 onto the stack.</li> <li><code>$"Hello, World!"$$</code> &mdash; Pushes the string <code>Hello, World!</code> onto the stack.</li> - <li><code>($0x1$$ $0x2$$ $0x3$$)</code> &mdash; Pushes the quotation <code>($0x1$$ $0x2$$ $0x3$$)</code> onto + <li><code>($!1$$ $!2$$ $!3$$)</code> &mdash; Pushes the quotation <code>($!1$$ $!2$$ $!3$$)</code> onto the stack. </li> </ul>

@@ -282,20 +282,20 @@ be <em>dequoted</em> through symbols like {{sym-.}}, which pushes all the items in a quotations

on the stack, one by one.</p> <p>Consider the following example hex program:</p> <pre><code> ($:dup$$ $:*$$ $:*$$) $"square"$$ $::$$ - $0x3$$ $:square$$ $:.$$ $:puts$$ $; prints 9$$</code></pre> + $!3$$ $:square$$ $:.$$ $:puts$$ $; prints 9$$</code></pre> <p>This program defines a symbol $:square$$ that can be used to calculate the square value of an integer, using the symbol {{sym-:}}. From then on, if $:square$$ is found anywhere in the same hex program, it will be substituted with <code>($:*$$ $:*$$)</code>. However, this is not enough to calculate the square value, because the logic to do so is in a quotation. To "execute" (dequote) a quotation, you must use the {{sym-.}} symbol, which pushes all the items in the quotation on the stack, which is equivalent to the following program:</p> - <pre><code> $0x3$$ $:dup$$ $:*$$ $:*$$ $:puts$$ $; prints 9$$</code></pre> + <pre><code> $!3$$ $:dup$$ $:*$$ $:*$$ $:puts$$ $; prints 9$$</code></pre> <p>While the {{sym-:}} symbol can be used to store quotations that can then be dequoted later using {{sym-.}}, typically you want to define operators which are <em>immediately dequoted</em> when pushed on the stack, thus behaving in a similar way as their native counterparts.</p> <p>You can achieve this using the {{sym-::}} symbol, and the previous example can be rewritten as follows:</p> <pre><code> ($:dup$$ $:*$$ $:*$$) $"square"$$ $:::$$ - $0x3$$ $:square$$ $:puts$$ $; prints 9$$</code></pre> + $!3$$ $:square$$ $:puts$$ $; prints 9$$</code></pre> <p>In this case, you no longer need to explicitly dequote $:square$$ using $:.$$, because it has been stored as an <em>operator</em> and hex knows it has to be immediately dequoted when pushed on the stack. </p>

@@ -391,9 +391,9 @@ <li>One byte representing the number of following bytes used to represent the integer (1 to 4).</li>

<li>Four bytes representing the signed integer value using two's complement, in little-endian format.</li> </ul> - <p>For example, the sequence <code>01 04 fe ff ff ff</code> represents the integer <code>-2</code> ($0xfffffe$$), + <p>For example, the sequence <code>01 04 fe ff ff ff</code> represents the integer <code>-2</code> ($!fffffe$$), and - the sequence <code>01 01 10</code> represents the integer 16 ($0x10$).</p> + the sequence <code>01 01 10</code> represents the integer 16 ($$10$).</p> <h5 id="pushstr">02 - PUSHST<a href="#top"></a></h5> <p>The <code>02</code> (PUSHST) opcode is used to push a string value onto the stack. The <code>02</code> opcode is

@@ -428,19 +428,19 @@ <p>The following sequence:</p>

<p> <code>03 05 02 04 74 65 73 74 01 01 01 38 3d 45</code> </p> - <p>represents the quotation <code>($"test"$$ $0x1$$ $:dec$$ $:cat$$ $:puts$$)</code></p> + <p>represents the quotation <code>($"test"$$ $!1$$ $:dec$$ $:cat$$ $:puts$$)</code></p> <h4 id="bytecode-example">Full Bytecode Example<a href="#top"></a></h4> <p>Consider the following hex program:</p> <pre><code>( $"_n"$$ $:$$ - ($:_n$$ $0x0$$ $:&lt;=$$) - ($0x1$$) - ($:_n$$ $:dup$$ $0x1$$ $:-$$ $:factorial$$ $:*$$) + ($:_n$$ $!0$$ $:&lt;=$$) + ($!1$$) + ($:_n$$ $:dup$$ $!1$$ $:-$$ $:factorial$$ $:*$$) $:if$$ $"_n"$$ $:#$$ ) $"factorial"$$ $:::$$ -$0x5$$ $:factorial$$ $:dec$$ $:puts$$</code></pre> +$!5$$ $:factorial$$ $:dec$$ $:puts$$</code></pre> <p>This gets compiled to the following bytecode:</p>

@@ -470,19 +470,19 @@ $; Push quotation of three items$$

03 03 $; Lookup first symbol (_n)$$ 00 00 00 - $; Push integer 0x0$$ + $; Push integer $0$$ 01 01 00 30 $; Symbol <=$$ $; Push quotation of one item$$ 03 01 - $; Push integer 0x1$$ + $; Push integer $1$$ 01 01 01 $; Push quotation of six items$$ 03 06 $; Lookup first symbol (_n)$$ 00 00 00 19 $; Symbol dup$$ - $; Push integer 0x1$$ + $; Push integer $1$$ 01 01 01 21 $; symbol -$$ $; Lookup second symbol (factorial)$$

@@ -502,7 +502,7 @@ 37 $; Symbol dec$$

44 $; Symbol puts$$</code></pre> <h3 id="native-symbols">Native Symbol Reference<a href="#top"></a></h3> - <p>hex provides a set of 64 ($0x40$$) native symbols that are built-in and pre-defined in the registry. The + <p>hex provides a set of 64 ($!40$$) native symbols that are built-in and pre-defined in the registry. The following section provides details on each of these symbols, including a signature illustrating how each symbol manipulates the stack.</p>

@@ -666,63 +666,63 @@ <h4 id="comparisons-symbols">Comparisons Symbols<a href="#top"></a></h4>

<h5 id="equal-symbol"><code>$:==$$</code> Symbol<a href="#top"></a></h5> <p><mark> a1 a2 &rarr; i</mark></p> <aside>OPCODE: <code>2b</code></aside> - <p>Pushes <code>0x1</code> on the stack if <code>a1</code> and <code>a2</code> are equal, or - <code>0x0</code> + <p>Pushes <code>$1</code> on the stack if <code>a1</code> and <code>a2</code> are equal, or + <code>$0</code> otherwise. </p> <h5 id="notequal-symbol"><code>$:!=$$</code> Symbol<a href="#top"></a></h5> <p><mark> i1 12 &rarr; i</mark></p> <aside>OPCODE: <code>2c</code></aside> - <p>Pushes <code>0x1</code> on the stack if <code>a1</code> and <code>a2</code> are not equal, or - <code>0x0</code> + <p>Pushes <code>$1</code> on the stack if <code>a1</code> and <code>a2</code> are not equal, or + <code>$0</code> otherwise. </p> <h5 id="greaterthan-symbol"><code>$:&gt;$$</code> Symbol<a href="#top"></a></h5> <p><mark> i1 12 &rarr; i</mark></p> <aside>OPCODE: <code>2d</code></aside> - <p>Pushes <code>0x1</code> on the stack if <code>i1</code> is greater than <code>i2</code>, or - <code>0x0</code> + <p>Pushes <code>$1</code> on the stack if <code>i1</code> is greater than <code>i2</code>, or + <code>$0</code> otherwise. </p> <h5 id="lessthan-symbol"><code>$:&lt;$$</code> Symbol<a href="#top"></a></h5> <p><mark> i1 12 &rarr; i</mark></p> <aside>OPCODE: <code>2e</code></aside> - <p>Pushes <code>0x1</code> on the stack if <code>i1</code> is less than <code>i2</code>, or <code>0x0</code> + <p>Pushes <code>$1</code> on the stack if <code>i1</code> is less than <code>i2</code>, or <code>$0</code> otherwise.</p> <h5 id="greaterthanequal-symbol"><code>$:&gt;=$$</code> Symbol<a href="#top"></a></h5> <p><mark> i1 12 &rarr; i</mark></p> <aside>OPCODE: <code>2f</code></aside> - <p>Pushes <code>0x1</code> on the stack if <code>i1</code> is greater than or equal to <code>i2</code>, or - <code>0x0</code> otherwise. + <p>Pushes <code>$1</code> on the stack if <code>i1</code> is greater than or equal to <code>i2</code>, or + <code>$0</code> otherwise. </p> <h5 id="lessthanequal-symbol"><code>$:&lt;=$$</code> Symbol<a href="#top"></a></h5> <p><mark> i1 i2 &rarr; i</mark></p> <aside>OPCODE: <code>30</code></aside> - <p>Pushes <code>0x1</code> on the stack if <code>i1</code> is less than or equal to <code>i2</code>, or - <code>0x0</code> otherwise. + <p>Pushes <code>$1</code> on the stack if <code>i1</code> is less than or equal to <code>i2</code>, or + <code>$0</code> otherwise. </p> <h4 id="boolean-logic-symbols">Boolean Logic Symbols<a href="#top"></a></h4> <h5 id="and-symbol"><code>$:and$$</code> Symbol<a href="#top"></a></h5> <p><mark> i1 i2 &rarr; i</mark></p> <aside>OPCODE: <code>31</code></aside> - <p>Pushes <code>0x1</code> on the stack if <code>i1</code> and <code>i2</code> are non-zero integers, or - <code>0x0</code> otherwise. + <p>Pushes <code>$1</code> on the stack if <code>i1</code> and <code>i2</code> are non-zero integers, or + <code>$0</code> otherwise. </p> <h5 id="or-symbol"><code>$:or$$</code> Symbol<a href="#top"></a></h5> <p><mark> i1 i2 &rarr; i</mark></p> <aside>OPCODE: <code>32</code></aside> - <p>Pushes <code>0x1</code> on the stack if <code>i1</code> or <code>i2</code> are non-zero integers, or - <code>0x0</code> otherwise. + <p>Pushes <code>$1</code> on the stack if <code>i1</code> or <code>i2</code> are non-zero integers, or + <code>$0</code> otherwise. </p> <h5 id="not-symbol"><code>$:not$$</code> Symbol<a href="#top"></a></h5> <p><mark> i &rarr; i</mark></p> <aside>OPCODE: <code>33</code></aside> - <p>Pushes <code>0x1</code> on the stack if <code>i</code> is zero, or <code>0x0</code> otherwise.</p> + <p>Pushes <code>$1</code> on the stack if <code>i</code> is zero, or <code>$0</code> otherwise.</p> <h5 id="xor-symbol"><code>$:xor$$</code> Symbol<a href="#top"></a></h5> <p><mark> i1 i2 &rarr; i</mark></p> <aside>OPCODE: <code>34</code></aside> - <p>Pushes <code>0x1</code> on the stack if <code>i1</code> and <code>i2</code> are different, or - <code>0x0</code> + <p>Pushes <code>$1</code> on the stack if <code>i1</code> and <code>i2</code> are different, or + <code>$0</code> otherwise. </p> <h4 id="type-checking-and-conversion-symbols">Type Checking and Conversion Symbols<a href="#top"></a></h4>

@@ -757,14 +757,14 @@ <p><mark> s &rarr; i</mark></p>

<aside>OPCODE: <code>39</code></aside> <p>Pushes the ASCII value of the string <code>s</code> on the stack.</p> <p>If <code>s</code> is longer than 1 character or if it is not representable using an ASCII code between - $0x0$$ + $!0$$ and - $0x7f$$, <code>$0xffffffff$$</code> is pushed on the stack.</p> + $!7f$$, <code>$!ffffffff$$</code> is pushed on the stack.</p> <h5 id="chr-symbol"><code>$:chr$$</code> Symbol<a href="#top"></a></h5> <p><mark> i &rarr; s</mark></p> <aside>OPCODE: <code>3a</code></aside> <p>Pushes the ASCII character represented by the integer <code>i</code> on the stack.</p> - <p>If <code>i</code> is not between $0x0$$ and $0x7f$$, an empty string is pushed on the stack.</p> + <p>If <code>i</code> is not between $!0$$ and $!7f$$, an empty string is pushed on the stack.</p> <h5 id="type-symbol"><code>$:type$$</code> Symbol<a href="#top"></a></h5> <p><mark> a &rarr; s</mark></p> <aside>OPCODE: <code>3b</code></aside>

@@ -790,7 +790,7 @@ <p><mark> (s a|q a) &rarr; i</mark></p>

<aside>OPCODE: <code>3f</code></aside> <p>Pushes the index of the first occurrence of the literal <code>a</code> in a string or a quotation on the stack. - If <code>a</code> is not found, <code>$0xffffffff$$</code> is pushed on the stack.</p> + If <code>a</code> is not found, <code>$!ffffffff$$</code> is pushed on the stack.</p> <h5 id="join-symbol"><code>$:join$$</code> Symbol<a href="#top"></a></h5> <p><mark> q s1 &rarr; s2</mark></p> <aside>OPCODE: <code>40</code></aside>