]> Lady’s Gitweb - Codemark/blob - sed/SYNTAXES/sh.sed
Initial commit
[Codemark] / sed / SYNTAXES / sh.sed
1 #!/usr/bin/sed -f
2 # SPDX-FileCopyrightText: 2025, 2026 Lady <https://www.ladys.computer/about/#lady>
3 # SPDX-License-Identifier: MPL-2.0
4
5 ## ⋯ 🧮🖍 Codemark ∷ sed ∷ SYNTAXES ∷ sh.sed
6 ##
7 ## ⁌ Shell script syntax
8 ##
9 ## ] Copyright © 2026 Lady [@ Ladys Computer].
10 ## ]
11 ## ] This Source Code Form is subject to the terms of the Mozilla
12 ## ] Public License, version 2.0.
13 ## ] If a copy of the M·P·L was not distributed with this file, You can
14 ## ] obtain one at {🔗<https://mozilla.org/MPL/2.0/>}.
15
16 ## § Implementation
17 ##
18 ## S·P·D·X copyright comments are made empty with·out replacement.
19 ## Rather, any copyright information should be expressed in prose.
20
21 /^# SPDX-/{
22 s/.*//
23 b
24 }
25
26 ## Documentation comments begin with two hashes.
27 ## The hashes are removed and the next cycle is begun.
28 ##
29 ## If the line which follows a documentation comment is blank, it is
30 ## kept that way rather than processed further.
31
32 /^##/{
33 :indoc
34 s/^## *//
35 n
36 /^##/b indoc
37 /^$/b
38 }
39
40 ## The behaviour of remaining blank lines varies depending on the line
41 ## which follows.
42 ## If it is not a documentation comment, a pipe is prepended.
43
44 /^$/{
45 N
46 /\n##/!s/^/|/
47 P
48 D
49 }
50
51 ## We can now assume that the current line did not begin with two
52 ## hashes; it should be treated as code.
53 ##
54 ## Lines which begin with (single) hashes are comments and not
55 ## processed further.
56 /^ *#/{
57 s/^\( *\)\(#.*\)/|\1⟦\2⟧/
58 b
59 }
60
61 ## An initial and final space is added to make processing easier.
62 ## These will be removed at the end.
63
64 s/^/ /
65 s/$/ /
66
67 ## Variable defaults must follow a colon `:´ command and be specified
68 ## either as a string or as a reference to another variable.
69
70 /^ *: "[$]{[^:}]*:=/{
71 s/"[$]{\([^:]*\):=\([^$"}]*\)}"/⟨"${⸤\1⸥:=\2}"⟩{@class="string"}/
72 s/"[$]{\([^:]*\):=\([$]{[^}]*}\)}"/⟨"${⸤\1⸥:=\2}"⟩{@class="string"}/
73 }
74
75 ## Control flow keywords such as `if´ or `for´ must begin their line.
76 ## They are handled upfront as they are easily recognized.
77 ##
78 ## For `then´, `else´, and `do´, following them with the no·op command
79 ## `:´ is recommended as a matter of style, but not required.
80
81 /^ *if [^ ]/s/if \([^ ]*\)/⦃︎if⦄︎ ⦃︎\1⦄︎/
82 /^ *then [^ ]/s/then \([^ ]*\)/⦃then⦄︎ ⦃︎\1⦄︎/
83 /^ *else [^ ]/s/else \([^ ]*\)/⦃else⦄︎ ⦃︎\1⦄︎/
84 /^ *fi /s/fi/⦃&⦄︎/
85 /^ *for [^ ][^ ]* in /s/for \([^ ]*\) in/⦃︎for⦄︎ ⸤†1⸥ ⦃︎in⦄︎/
86 /^ *do [^ ]/s/do \([^ ]*\)/⦃do⦄︎ ⦃︎\1⦄︎/
87 /^ *done /s/done/⦃&⦄︎/
88
89 ## It is not permitted to pipe into a control flow keyword.
90 ## So, after pipes, the script will jump here to begin processing.
91
92 :postpipe
93
94 ## When a line begins with the sequence `)"´, it is assumed to be
95 ## closing a subshell.
96 ## In this case, the script skips ahead to avoid recognizing this
97 ## sequence as a command.
98
99 /^ *)"/b postback
100
101 ## The first word which is not a variable assignment is assumed to be
102 ## the name of a command, unless this line already contains markup
103 ## from control flow processing.
104
105 /⦃︎/!s/ \([^ =]\{1,\}\) / ⦃︎\1⦄︎ /
106
107 ## Variables must be processed in a loop because there can be multiple
108 ## on a line.
109 ## However, they are only processed up to the first instance of `⦃︎´
110 ## markup (presumably inserted by the above rules).
111
112 :variable
113 /^\([^=]*\([^A-Za-z][^0-9A-Za-z_]*=\)*\)*⦃︎/!{
114 s/ \([A-Za-z][0-9A-Za-z_]*\)=/ ⸤\1⸥=/
115 / [A-Za-z][0-9A-Za-z_]*=/b variable
116 }
117
118 ## After backslashes (line continuations) or subshells, it is assumed
119 ## that the line is in the middle of a command.
120 ## By jumping here, the program skips variable and command name
121 ## processing.
122
123 :postback
124
125 ## After `&&´, `||´, and `|´, the following word is a command.
126
127 s/ \(&&\) \([^ ]*\) / \1 ⦃\2⦄︎ /
128 s/ || \([^ ]*\) / || ⦃\1⦄︎ /
129 s/ | \([^ ]*\) / | ⦃\1⦄︎ /
130
131 ## Some rules are enforced for strings :⁠—
132 ##
133 ## • When a string wraps a variable reference, it must have the form
134 ## `"${VARIABLE_NAME}"´ (the variable must be the only thing in the
135 ## string).
136 ##
137 ## • When a string wraps a subshell, the text of the subshell
138 ## invocation must be on its own line.
139 ## The string must not contain anything aside from the subshell; it
140 ## must begin `"$(´ and end `)"´.
141 ##
142 ## • Strings consisting of only a straight single quote must be
143 ## double‐quoted.
144 ##
145 ## • Otherwise, strings must be single‐quoted.
146 ##
147 ## The shell syntax allows strings to be concatenated by placing them
148 ## directly adjacent to each other, so a variable reference and a
149 ## literal value may be joined together in this manner.
150
151 s/"'"/⟨"'"⟩{@class="string"}/g
152 s/"${\([^}]*\)}"/⟨"${\1}"⟩{@class="string"}/g
153 /^ *)"/s/)"/⟨)"⟩{@class="string"}/g
154 s/"$(/⟨"$(⟩{@class="string"}/g
155 s/\('[^']*'\)/⟨\1⟩{@class="string"}/g
156
157 ## The initial and final spaces added above can now be removed.
158
159 s/^ //
160 s/ $//
161
162 ## Finally, a `|´ can be prepended to mark the line as preformatted.
163
164 s/^/|/
165
166 ## If the current line of code ends in a backslash or a pipe, the
167 ## following line is a continuation of it.
168 ## The `n´ command manually advances to the next line with·out starting
169 ## a new cycle, and the program then jumps to the correct place for
170 ## what might follow.
171
172 /[\]$/{
173 n
174 s/^/ /
175 s/$/ /
176 b postback
177 }
178 /|$/{
179 n
180 s/^/ /
181 s/$/ /
182 b postpipe
183 }
This page took 0.055159 seconds and 5 git commands to generate.