a glob of sed — expanding shell globs with sed


s/@/@0/g;s/>/@1/g;s/\[/@2/g;s/\]/@3/g;x
s/^.*$/>/;x;:l;s/(>>+)([^,{}<>]),/,\1\2/
tl;s/(>)([^,{}<>]),/,\2\1\2/;tl
s/(>+)([^,{}<>])([^,{}<>])/\3\1\2/;tl
s/>(>+)([^,{}<>])}/}\1\2/;tl
s/>([^,{}<>])\}/\}/;tl
s/([^>])(>+)([^,{}<>])\{/\1\{\2>\3/;tl
s/([^,{}<>])\{/\{\1>\1/;tl
/^{.*\[/{y/[']/{,}/;bl;}
s/^{(.*)}{/>{\1}{/;tm;bM
:m;s/>}{/]{/;tl;s/>{/[>/;tm;s/>}/]>/
tm;s/>,/'>/;tm;s/>(.)/\1>/;tm;:M
s/^{(.*)}$/\1/;tl;:R;s/^/>/;:r
s/>(.*)(.)/\2>\1/;tr;s/>$//;y/{}/}{/
x;y/<>/></;x;/^[^{].*}$/bl;x;/</{x;bR;};x
s/[{}]//g;s/@1/>/g;s/@2/[/g;s/@3/]/g;s/@0/@/g

What?


the 545 bytes above are program to expand a subset of shell globs, specifically those using braces and commas to specify alternatives. for example, here is one that expands to all the urls that this page is accessible at:

{gemini,http{s,}}://binarycat.flounder.online/gemlog/2022-03-08_a-glob-of-sed.gmi

How?


here is the commented version:

(also contains some "assertions")


#!/bin/sed -f

# > pushes a character to the right
# having multiple signifies a greater depth

# escape certain chars to be used internally
s/@/@0/g
s/>/@1/g
s/\[/@2/g
s/\]/@3/g

# we use the hold area to store whether we are currently in a "reversed" state.
# this is just used so we don't print the output backwards.
x
s/^.*$/>/
x

# start of main loop
:l

# debug print
#p

# assert that no more than one cascade exists
/>[^>]+>/ {
	s/^.*$/ERROR/
	q
}

# pull cascading charachter over a comma (level > 1)
s/(>>+)([^,{}<>]),/,\1\2/
t l

# pull cascading characher over a comma, leaving behind a copy (level == 1)
s/(>)([^,{}<>]),/,\2\1\2/
t l

# advance cascading characher
s/(>+)([^,{}<>])([^,{}<>])/\3\1\2/
t l

# cascade exits brace, loses a level (if level > 1)
s/>(>+)([^,{}<>])}/}\1\2/
t l

# cascade hits brace and ends (level == 1)
s/>([^,{}<>])\}/\}/
t l

# increase the depth of an existing cascade
s/([^>])(>+)([^,{}<>])\{/\1\{\2>\3/
t l

# assert that no cascade exists
/>/ {
	s/^.*$/ERROR/
	q
}

# start cascading a new charachter through the expansions, leave behind a copy
s/([^,{}<>])\{/\{\1>\1/
t l

# here we multiply adjacent arrays.
# this is quite tricky, and requires almost doubling the amount of metachars used
# basically, we convert all metachars in the left array to
# an alternate form, then let the rest of the program
# move them to where they need to be, then convert them back.

# the final step, convert everything back
/^{.*\[/ {
	y/[']/{,}/
	b l
}

# if there are adjacent arrays, mark the start and begin converting
s/^{(.*)}{/>{\1}{/
t m
# otherwise, continue with other tests.
b M

# loop to convert metachars
:m

# more debug prints
#p

# exit condition: converting the final brace
s/>}{/]{/
t l

# do conversion
s/>{/[>/;t m
s/>}/]>/;t m
s/>,/'>/;t m

# leave other chars unchanged
s/>(.)/\1>/;t m

a\
unreachable

q

# done with array multiplication
:M

# remove outer redundant braces
s/^{(.*)}$/\1/
t l

#p

# start reversing
:R
# delimiter between reversed and unreversed parts (reusing a metachar)
s/^/>/

# start reversal loop
:r
s/>(.*)(.)/\2>\1/
t r
# end reversal loop

# remove delimiter
s/>$//

y/{}/}{/

# update stored direction
x
y/<>/></
x

# done reversing

# brace at end but not start, cascade more chars
/^[^{].*}$/ b l

# if the pattern space is backwards, reverse it.
x
/</ {
	x
	b R
}
x

# remove leftover braces
s/[{}]//g

# unescape internal metachars
s/@1/>/g
s/@2/[/g
s/@3/]/g
# this one must be done last
s/@0/@/g

if you uncomment some of the print statements (or add you own!), you can actually see the program shuffling chars around.


Bugs?


quite likely.




home

gemlog index



/gemlog/