ulas: Added line escaping
authorLukas Krickl <lukas@krickl.dev>
Wed, 31 Dec 2025 05:17:29 +0000 (06:17 +0100)
committerLukas Krickl <lukas@krickl.dev>
Wed, 31 Dec 2025 05:19:44 +0000 (06:19 +0100)
Lines can now be escape using '\'.
When the last printable character of a line is '\' the next line is read
before executing the preprocessor. This allows breaking up lines into
more readable variants.

TODO.md
makefile
src/test.c
src/ulas.c
tests/multiline.bin [new file with mode: 0644]
tests/multiline.s [new file with mode: 0644]

diff --git a/TODO.md b/TODO.md
index 4820496d0f09b7bc227108cd90694816292c1a96..cd055f752c517e5b3feb6e6fe992002f13547683 100644 (file)
--- a/TODO.md
+++ b/TODO.md
@@ -9,7 +9,6 @@
 - Assert `.section` in full test
 - Add `.bank.` directive allowing users to set the current bank number (ulas.bank) which can be used for symbol files.
 - Allow nested begin and end scope calls
-- Add `forth-like` mode. This mode should allow defining words similar to forth. It should have a built-in word for `assembly output` that can be implemented by the user.
 - Add `.extern` directive allowing the user to define external labels
 - Add `.scope` for `.def` and `.de.` variables
 - Add `#cop n, ...` preprocessor directive. It should work just like .rep but just insert the line n times into the source code and then preprocess the result.
index 7badfd825a4355bf1d11639d23ad2a9401be25c7..4b66e384dcae1d1b514f938bba97d2d827684c81 100644 (file)
--- a/makefile
+++ b/makefile
@@ -52,4 +52,5 @@ install:
 
 buildtests: bin 
        ./$(NAME) tests/t0.s -D ULAS_PREDEF -l - -o tests/t0.bin
+       ./$(NAME) tests/multiline.s -D ULAS_PREDEF -l - -o tests/multiline.bin
        ./$(NAME) tests/t0.bin -d - -o tests/t0_dasm.s
index 0fa1c239e6482f77d71720de779e95ce3d045191..553d5f851dc19727b6cf79a05a642efbda082c7c 100644 (file)
@@ -421,6 +421,7 @@ void test_full_asm(void) {
   TESTBEGIN("testfullasm");
 
   ASSERT_FULL_ASM(0, "tests/t0.s", "tests/t0.bin");
+  ASSERT_FULL_ASM(0, "tests/multiline.s", "tests/multiline.bin");
 
   TESTEND("testfullasm");
 }
index 19894badbb7e52ae6d10f84491572ac69dc0c34d..9f1d3d8426057bd6bed215c5a4fc3af8f03fd78a 100644 (file)
@@ -1290,11 +1290,61 @@ found:
   return ULAS_PPDIR_NONE;
 }
 
+// checks if the last printable char in a string is '\'
+// side-effect: also replaced that what with \0 if it is found
+int ulas_last_print_is_escape(char *buf, int n) {
+       int found_escape = 0;
+       // check if last printable char is an escape char
+       unsigned int buf_len = strnlen(buf, n);
+       if (buf_len == 0) {
+               return 0;
+       }
+
+       for (int i = buf_len - 1; i >= 0; i--) {
+               if (isprint(buf[i])) {
+                       found_escape = buf[i] == '\\';
+                       if (found_escape) {
+                               buf[i] = '\0';
+                       }
+                       break;
+               }
+       }
+
+       return found_escape;
+}
+
+// call fgets and concats into buf until either buf is full (fatal error)
+// or a non-escape character is *not* the last chracter of a line
+// returns number of lines read
+int ulas_fgets(char *buf, int n, FILE *src) {
+       char internal_buf[ULAS_LINEMAX+1];
+       buf[0] = '\0';
+       internal_buf[ULAS_LINEMAX] = '\0';
+
+       int lines_read = 0;
+
+       do {
+               char *res = fgets(internal_buf, ULAS_LINEMAX, src);
+
+               // bail early
+               if (res == NULL) {
+                       break;
+               }
+               
+               strncat(buf, internal_buf, n);
+
+               lines_read++;
+       } while(ulas_last_print_is_escape(buf, n));
+
+       return lines_read;
+}
+
 int ulas_preprocnext(struct ulas_preproc *pp, FILE *dst, FILE *src, char *buf,
                      int n) {
   int rc = 1;
-  if (fgets(buf, n, src) != NULL) {
-    ulas.line++;
+       int lines_read = 0;
+  if ((lines_read = ulas_fgets(buf, n, src)) != 0) {
+    ulas.line += lines_read;
 
     unsigned long buflen = strlen(buf);
 
@@ -2707,7 +2757,7 @@ fail:
 
 int ulas_asmnext(FILE *dst, FILE *src, char *buf, int n) {
   int rc = 1;
-  if (fgets(buf, n, src) != NULL) {
+  if (ulas_fgets(buf, n, src) != 0) {
     unsigned long buflen = strlen(buf);
     if (ulas_asmline(dst, src, buf, buflen) == -1) {
       rc = -1;
diff --git a/tests/multiline.bin b/tests/multiline.bin
new file mode 100644 (file)
index 0000000..955058a
Binary files /dev/null and b/tests/multiline.bin differ
diff --git a/tests/multiline.s b/tests/multiline.s
new file mode 100644 (file)
index 0000000..65bbcb7
--- /dev/null
@@ -0,0 +1,17 @@
+#define test 123\
+456
+
+ld b\
+       , b
+nop
+ld hl, test
+
+ld b\
+\
+\
+, b
+
+.str "hello\
+world"
+
+nop