Search code examples
arraystext-filesforthgforth

Writing a text file into an array on Forth


I have a text file, containing an array of numbers such as:

1 0 0 1 0
0 1 1 0 1
0 0 0 1 1
1 1 0 0 0
0 0 1 0 0

I have opened the text file with the following code:

variable file-id
: open_file ( -- ) \ Opens the text file
  s" c:\etc\textfile.txt" r/w open-file throw
  file-id ! ;

I have also created an array to store this data:

create an_array 25 chars allot \ Create the array

: reset_array ( -- ) big_array 25 0 fill ;
reset_array \ Set all elements to 0

Is there a way to write the contents of the text file into the array with Forth?


Solution

  • 1. Lazy way

    A lazy way is to just evaluate a file by performing included on the file name.

    \ Some library-level common words
    \ --------------------------------
    
    \ Store n chars from the stack into the array at c-addr, n >= 0
    : c!n ( n*x n c-addr -- )
      >r begin dup while ( i*x i )
        \ store top from i*x into addr+(i-1)*char_size
        1- tuck chars r@ + c! ( j*x j )
      repeat drop rdrop
    ;
    
    \ Execute xt and produce changing in the stack depth
    : execute-balance ( i*x xt -- j*x n ) depth 1- >r execute depth r> - ;
    
    : included-balance ( i*x c-addr u -- j*x n )
      ['] included execute-balance 2 +
    ;
    
    \ The program
    \ --------------------------------
    
    create myarray 25 chars allot \ Create the array
    
    : read-myfile-numbers ( -- i*x i )
      state @ abort" Error: only for interpretation"
      s" ./mynumbers.txt" included-balance
    ;
    : write-myarray ( i*x i -- )
      dup 25 <> abort" Error: exactly 25 numbers are expected"
      myarray c!n
    ;
    : fill-myarray-from-myfile ( -- )
      read-myfile-numbers write-myarray
    ;
    

    2. Tidy way

    A careful way is to read a file (completely, or line by line), split the text into lexemes, convert lexemes into numbers, then store the numbers into your array.

    See: How to enter numbers in Forth .

    On a low level it can be done via read-file or read-line, something like word|tail and >number (or something like StoN library word from the example above).

    On a higher level: use Gforth specific words like execute-parsing or execute-parsing-file, parse-name and s>number?

    : next-lexeme ( -- c-addr u|0 )
      begin parse-name ?dup if exit then ( addr )
        refill 0= if 0 exit then drop
      again
    ;
    : read-myfile-numbers ( -- i*x i )
      s" ./mynumbers.txt" r/o open-file throw
      [:
        0 begin next-lexeme dup while
          s>number? 0= abort" Error: NaN" d>s swap 1+
        repeat 2drop
      ;] execute-parsing-file
    ;
    
    

    If you need to read too many numbers, you have to write them into the array one by one at once, instead of placing all of them into the stack.