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