\ 2022 - winduptoy.sensorstation.co \ PUBLIC DOMAIN \ USAGE \ $ cat in.gmi | gforth gmi2html.fs > out.html \ \ This only outputs major blocks, allowing full customization of the HTML document. \ Best used in composition, like so: \ \ $ cat _header.html > out.html \ $ cat in.gmi | gforth gmi2html.fs >> out.html \ $ cat _footer.html >> out.html \ === HTML Output === \ \ : html-line-break ."
" ; : html-line-break ." " ; : html-blockquote-open ."
" ; : html-blockquote-close ."
" ; : html-preformatted-open ( c-content u -- ) .\"
" cr ;

: html-preformatted-close ." 
" cr ; : html-list-open ." " ; : html-paragraph-open ."

" ; : html-paragraph-close ."

" ; : html-list-item ( c-content u -- ) ."
  • " type ."
  • " ; : html-link ( c-content u c-url u -- ) .\"

    " type ."

    " ; : html-heading ( level c-content u -- ) ." " type ." " ; \ === Parsing === \ : starts-preformatted? ( c-addr u -- f ) s" ```" string-prefix? ; : starts-blockquote? ( c-addr u -- f ) s" >" string-prefix? ; : starts-list-item? ( c-addr u -- f ) s" *" string-prefix? ; : starts-link? ( c-addr u -- f ) s" =>" string-prefix? ; : starts-heading? ( c-addr u -- n ) \ returns header level 0 rot rot 3 min 0 ?do dup i + c@ '# = if swap 1 + swap endif loop drop ; 4096 constant line-buffer-size create line-buffer line-buffer-size chars allot variable line-number variable line-len 0 constant capture-none 1 constant capture-preformatted 2 constant capture-blockquote 3 constant capture-list variable capture-state : process-line ( -- ) capture-state @ case capture-preformatted of line-buffer line-len @ starts-preformatted? if html-preformatted-close capture-none capture-state ! exit else line-buffer line-len @ type cr exit endif endof capture-blockquote of line-buffer line-len @ starts-blockquote? invert if html-blockquote-close capture-none capture-state ! endif endof capture-list of line-buffer line-len @ starts-list-item? invert if html-list-close capture-none capture-state ! endif endof endcase line-buffer line-len @ starts-preformatted? if capture-preformatted capture-state ! line-buffer line-len @ '` skip html-preformatted-open exit endif line-buffer line-len @ starts-blockquote? if capture-state @ capture-blockquote = invert if html-blockquote-open capture-blockquote capture-state ! endif line-buffer 1 + line-len @ 1 - type html-line-break cr exit endif line-buffer line-len @ starts-list-item? if capture-state @ capture-list = invert if html-list-open capture-list capture-state ! endif line-buffer 1 + line-len @ 1 - html-list-item cr exit endif line-buffer line-len @ starts-heading? dup if line-buffer line-len @ '# skip 32 skip 9 skip html-heading cr exit endif drop line-buffer line-len @ starts-link? if line-buffer line-len @ '= skip '> skip 32 skip 9 skip ( c-addr len ) \ find end of URL over over 32 scan ( c-addr len label-addr label-len ) >r dup >r 2 pick - swap drop ( c-addr url-len ) r> r> 32 skip 9 skip ( c-addr url-len label-addr label-len ) dup 0 = if \ use the URL for the label if no label is provided drop drop over over endif 2swap html-link exit endif line-len @ 0 = if html-line-break cr else html-paragraph-open line-buffer line-len @ type html-paragraph-close cr endif ; : gmi-to-html begin 1 line-number +! line-buffer line-buffer-size stdin read-line ( len flag err ) throw invert if drop exit endif \ false flag == eof line-len ! process-line again ; gmi-to-html depth throw \ ensure stack is clean bye