about summary refs log tree commit diff stats
path: root/forth/gmi2html.fs
blob: 1a08ccf633eb3d4e15c2e5c0bb9ca336fd9d5772 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
\ 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 ." <br>" ;

: html-line-break ." " ;
: html-blockquote-open ." <blockquote>" ;
: html-blockquote-close ." </blockquote>" ;

: html-preformatted-open ( c-content u -- )
	.\" <pre aria-label=\"" type .\" \">" cr ;

: html-preformatted-close ." </pre>" cr ;

: html-list-open ." <ul>" ;
: html-list-close ." </ul>" ;

: html-paragraph-open ." <p>" ;
: html-paragraph-close ." </p>" ;

: html-list-item ( c-content u -- )
	." <li>" type ." </li>" ;


: html-link ( c-content u c-url u -- )
	.\" <p><a rel=\"noreferrer\" href=\"" type .\" \">"
	type
	." </a></p>"
	;

: html-heading ( level c-content u -- )
	." <h" 2 pick 48 + emit ." >"
	type
	." </h" 48 + emit ." >"
	;

\ === 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