diff options
author | Case Duckworth | 2024-01-29 00:02:27 -0600 |
---|---|---|
committer | Case Duckworth | 2024-01-29 00:02:27 -0600 |
commit | 59e5a214a8be015b00004b1961a83f48dd6d65f4 (patch) | |
tree | 1cbe20804bc478a882039ef2b46293c781004fe2 | |
download | subtext-59e5a214a8be015b00004b1961a83f48dd6d65f4.tar.gz subtext-59e5a214a8be015b00004b1961a83f48dd6d65f4.zip |
Initial commit
-rw-r--r-- | subtext.awk | 165 | ||||
-rw-r--r-- | test.st | 33 |
2 files changed, 198 insertions, 0 deletions
diff --git a/subtext.awk b/subtext.awk new file mode 100644 index 0000000..c3e135b --- /dev/null +++ b/subtext.awk | |||
@@ -0,0 +1,165 @@ | |||
1 | # subtext -- text substitutor -*- awk -*- | ||
2 | # (C) C Duckworth <acdw@acdw.net> | ||
3 | |||
4 | BEGIN { | ||
5 | ## Tuneables | ||
6 | postproc = get_value(postproc, "ST_POSTPROC", "shexpand") | ||
7 | bodyfunc = get_value(bodyfunc, "ST_BODYFUNC", "sub_text") | ||
8 | sopath = get_value(sopath, "ST_SOPATH", ".:"ENVIRON["HOME"]"/.subtext") | ||
9 | split(sopath, asopath, ":") | ||
10 | # postproc = postproc ? postproc : "shexpand" | ||
11 | # bodyfunc = bodyfunc ? bodyfunc : "sub_text" | ||
12 | ## Globals | ||
13 | # Booleans | ||
14 | true = 1 | ||
15 | false = 0 | ||
16 | ## Wrap the text in a function | ||
17 | pretext = "### begin text\n"bodyfunc"(){\n" | ||
18 | posttext = "}\n### end text" | ||
19 | ## Prelude function strings | ||
20 | # Ask sed to do these b/c awk has no capture groups ;_; | ||
21 | shellfix = "sed -E" \ | ||
22 | " -e 's/`/\\\\`/g'" \ | ||
23 | " -e 's/(^|[^\\$])\\$([^\\$]|$)/\\1\\\\$\\2/g'" \ | ||
24 | " -e 's/(^|[^\\$])\\$(\\$+)([^\\$]|$)/\\1\\2\\3/g'" | ||
25 | shxwrap = " -e 's/^/:/'" | ||
26 | htmlfix = "sed -E" \ | ||
27 | " -e 's#([^\\\\]|^)&#\\1\\&#g'" \ | ||
28 | " -e 's#([^\\\\]|^)<#\\1\\<#g'" \ | ||
29 | " -e 's#([^\\\\]|^)>#\\1\\>#g'" \ | ||
30 | " -e 's#\\\\([&<>])#\\1#g'"; | ||
31 | ## Prelude | ||
32 | par = "#!/bin/sh\n### kernel\n" \ | ||
33 | "preface()(sed \"s/^/$1/\")\n" \ | ||
34 | "unpreface()(sed \"s/^$1//\")\n" \ | ||
35 | "shexpand()(eval \"$( (echo 'cat<<.';preface +;echo .)" \ | ||
36 | " | unpreface + )\")\n" \ | ||
37 | "### library\n" \ | ||
38 | "shellfix()(" shellfix ")\n" \ | ||
39 | "htmlfix()(" htmlfix ")\n" \ | ||
40 | "### variables\n" \ | ||
41 | "ST_POSTPROC=" postproc "\n" \ | ||
42 | "ST_BODYFUNC=" bodyfunc "\n" \ | ||
43 | "ST_SOPATH=" sopath "\n" \ | ||
44 | "### header\n" | ||
45 | } | ||
46 | |||
47 | ### End a block | ||
48 | |||
49 | end[endn] && $0 == end[endn] { | ||
50 | par = par "\n" end[endn--] "\n)" | ||
51 | printpar() | ||
52 | subdocp = false | ||
53 | next | ||
54 | } | ||
55 | |||
56 | ### Line continuation | ||
57 | |||
58 | /\\$/ { | ||
59 | getline nl | ||
60 | sub(/\\$/,"") | ||
61 | $0 = $0 " " nl | ||
62 | } | ||
63 | |||
64 | ### Special commands | ||
65 | ## These call subtext-internal functions | ||
66 | |||
67 | /^\.so/ { # Insert $2 verbatim (if in sopath), else error | ||
68 | source($2) | ||
69 | next | ||
70 | } | ||
71 | |||
72 | ### Escape sequences | ||
73 | |||
74 | /^\.\.\./ { # Delimit document (end is optional) | ||
75 | printpar() | ||
76 | docp = !docp | ||
77 | if (docp) print pretext "unpreface ':'<<'_'|eval \"$ST_POSTPROC\"" | ||
78 | else end_text() | ||
79 | next | ||
80 | } | ||
81 | |||
82 | /^\.\./ && docp { # Begin a heredoc | ||
83 | # ..[<command>] [<options>] [<<delim] | ||
84 | ## ends with DELIM or '..' | ||
85 | subdocp = true | ||
86 | end[++endn] = (match($0,"<<") ? substr($0,RSTART+RLENGTH) : "..") | ||
87 | command = substr($0, 3, RSTART ? RSTART - 2 : length) | ||
88 | $0 = "$$(" (command ? command : "cat") "<<" end[endn] | ||
89 | } | ||
90 | |||
91 | /^\./ && docp { # One-line command | ||
92 | # .<command> [<parameters>] | ||
93 | ## wraps the line in $( ... ), basically (also quotes) | ||
94 | specialp = 2 | ||
95 | gsub(/"/,"\\\\&") | ||
96 | ln = "$$(" substr($1, 2) | ||
97 | for (f=2;f<=NF;f++) ln = ln " \"" $f "\"" | ||
98 | ln = ln ")" | ||
99 | $0 = ln | ||
100 | } | ||
101 | |||
102 | /^\\/ && docp { # \ at the beginning of a line escapes the next character | ||
103 | $0 = substr($0, 2) | ||
104 | } | ||
105 | |||
106 | ### Book-keeping | ||
107 | |||
108 | /^$/ { | ||
109 | if (!par) next | ||
110 | printpar() | ||
111 | } | ||
112 | |||
113 | { par = par (par?"\n":"") $0 } | ||
114 | { if (--specialp < 0) specialp = 0 } | ||
115 | |||
116 | END { | ||
117 | if (dead) exit dead | ||
118 | if (par) printpar() | ||
119 | while (endn > 0) | ||
120 | print "\n" end[endn--] "\n)" | ||
121 | if (docp) end_text() | ||
122 | } | ||
123 | |||
124 | function end_text() { | ||
125 | print "_\n" posttext | ||
126 | } | ||
127 | |||
128 | function printpar() { | ||
129 | specialp = specialp || (match(par, /^[ ]*</)) | ||
130 | if (docp) { | ||
131 | if (!subdocp && !specialp) par = "<p>" par "</p>" | ||
132 | shx = shellfix shxwrap | ||
133 | print par | shx | ||
134 | close(shx) | ||
135 | } else print par | ||
136 | par = "" | ||
137 | } | ||
138 | |||
139 | function get_value(var, env_var, default) { | ||
140 | if (var) return var | ||
141 | else if (ENVIRON[env_var]) return ENVIRON[env_var] | ||
142 | else return default | ||
143 | } | ||
144 | |||
145 | function source(name) { | ||
146 | found = false | ||
147 | sp = "" | ||
148 | for (dir in asopath) { | ||
149 | fn = asopath[dir] "/" name | ||
150 | sp = asopath[dir] "\n\t" sp | ||
151 | while ((getline ln < fn) > 0) { | ||
152 | found = true | ||
153 | par = par (par?"\n":"") ln | ||
154 | } | ||
155 | if (found) break | ||
156 | } | ||
157 | if (!found) | ||
158 | die("Couldn't source " name "; looked in:\n\t" sp, 9) | ||
159 | } | ||
160 | |||
161 | function die(message, code) { | ||
162 | dead = code | ||
163 | print "!!" FILENAME ":" NR ": " message > "/dev/stderr" | ||
164 | exit | ||
165 | } | ||
diff --git a/test.st b/test.st new file mode 100644 index 0000000..b11bd87 --- /dev/null +++ b/test.st | |||
@@ -0,0 +1,33 @@ | |||
1 | .so etlib.sh | ||
2 | alias verse=cat | ||
3 | title="test file" | ||
4 | ... | ||
5 | here's a test file | ||
6 | .a href="https://example.com" a link or something!! | ||
7 | |||
8 | .h1 it has "headers" or whatever | ||
9 | |||
10 | ..blockquote title=foo | ||
11 | it has paragraphs of quotes | ||
12 | .. | ||
13 | |||
14 | $$(echo it has shell $scripty-stuff) | ||
15 | |||
16 | .. | ||
17 | escaped par! | ||
18 | omg wow | ||
19 | .. | ||
20 | |||
21 | ..p | ||
22 | another one :O | ||
23 | |||
24 | holy crap | ||
25 | this is great | ||
26 | .. | ||
27 | |||
28 | i should have more paragraphs | ||
29 | |||
30 | they are wonderful | ||
31 | i love them | ||
32 | |||
33 | very much | ||