From c7f64d648ed75231c27049bd2afaa0bf6f7f662c Mon Sep 17 00:00:00 2001 From: Steve Kemp Date: Sun, 27 Sep 2026 12:03:07 +0300 Subject: [PATCH] Implement `strat`. This allows accessing each byte of a string, by offset, regardless of whether there are embedded NULL or other binary characters. NULL bytes `0x00` otherwise stop `substr` and `explode` from working as expected. This closes #254. --- PRIMITIVES.md | 3 +++ compiler/template.tmpl | 34 ++++++++++++++++++++++++++++++++++ inception.lisp | 1 + stdlib.slisp | 6 ++++++ 4 files changed, 44 insertions(+) diff --git a/PRIMITIVES.md b/PRIMITIVES.md index c63b6ac..b700288 100644 --- a/PRIMITIVES.md +++ b/PRIMITIVES.md @@ -175,6 +175,9 @@ added to them too. * Returns file information as a list (TYPE SIZE MODE), or nil on failure. * `stdlib` * Return the contents of our slisp stdlib at runtime. + * `strat` + * Read the byte at offset N from the given string. + * This is a dangerous function, with no bounds-checking, which works around the fact `explode` and `substr` get foiled by embedded NULL characters. * `strcat` * Join two strings together and return them. * `strcmp` diff --git a/compiler/template.tmpl b/compiler/template.tmpl index fc5dc1d..0081cf9 100644 --- a/compiler/template.tmpl +++ b/compiler/template.tmpl @@ -2770,6 +2770,40 @@ FUNC fn_sys_stdlib TAG_STRING_REG rax ret +;; Return the given byte from the string, as an integer. +;; +;; This function is dangerous as it has no bound checking, we could add +;; it since we know the size of all allocations made with alloc.. +;; +;; For the moment it is a workaround for the fact our strings can't have +;; embedded null-characters - if they do "explode" and "substr" don't work +;; any longer. +;; +;; TODO: Add bound-checking. +;; +FUNC fn_sys_strat + mov rbx, rdi ; type-check arg1 + GET_TAG_BITS rbx + cmp rbx, TAG_ID_STRING + jne type_error + mov rbx, rsi ; type-check arg2 + GET_TAG_BITS rbx + cmp rbx, TAG_ID_INTEGER + jne type_error + + UNTAG_REG rdi + UNTAG_REG rsi + + ; RDI points to the string, add the RSI/offset to it + add rdi, rsi + + ; Load and return the character/byte as an integer. + xor rax, rax + mov al, byte [rdi] + TAG_INTEGER_REG rax + ret + + ;; join the two given strings and return a new one. FUNC fn_sys_strcat mov rbx, rdi diff --git a/inception.lisp b/inception.lisp index 1a6c537..78ee35e 100644 --- a/inception.lisp +++ b/inception.lisp @@ -336,6 +336,7 @@ (register-builtin "sys_sqrt" (lambda (args) (sys_sqrt (car args)))) (register-builtin "sys_stat" (lambda (args) (sys_stat (car args)))) (register-builtin "sys_stdlib" (lambda (args) (sys_stdlib))) + (register-builtin "sys_strat" (lambda (args) (sys_strat (car args) (cadr args)))) (register-builtin "sys_strcat" (lambda (args) (sys_strcat (car args) (cadr args)))) (register-builtin "sys_strcmp" (lambda (args) (sys_strcmp (car args) (cadr args)))) (register-builtin "sys_strdup" (lambda (args) (sys_strdup (car args)))) diff --git a/stdlib.slisp b/stdlib.slisp index 41f3f29..27c98fc 100644 --- a/stdlib.slisp +++ b/stdlib.slisp @@ -213,6 +213,12 @@ This returns a list of the form (TYPE SIZE MODE)." "Return the contents of the stdlib.slisp standard-library." (sys_stdlib)) +(defun strat(str n) + "Return the integer value of the character at offset N for the given string. + +This ignores embedded NULLs which would otherwise break substr and explode." + (sys_strat str n)) + (defun strcat(a b) "Return the result of concatenating the two specified strings into one." (sys_strcat a b))