summaryrefslogtreecommitdiffstats
path: root/rsh.go
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2026-08-24 22:49:01 -0700
committerRose Hogenson <rosehogenson@posteo.net>2026-08-24 22:49:01 -0700
commitec47b533e2d748f6831f0fd2f68e93d0954c1d8f (patch)
tree3f456337a0fafbf51fda17ac52e2102ddcf47468 /rsh.go
downloadrsh-ec47b533e2d748f6831f0fd2f68e93d0954c1d8f.tar.zst
Initial commitHEADmain
Diffstat (limited to 'rsh.go')
-rw-r--r--rsh.go182
1 files changed, 182 insertions, 0 deletions
diff --git a/rsh.go b/rsh.go
new file mode 100644
index 0000000..80c52e0
--- /dev/null
+++ b/rsh.go
@@ -0,0 +1,182 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+)
+
+type lexer struct {
+ insertConcat bool
+}
+
+func (l *lexer) splitTokens(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ n := 0
+Whitespace:
+ for ; n < len(data); n++ {
+ switch data[n] {
+ case ' ', '\t':
+ l.insertConcat = false
+ case '#':
+ i := n + 1
+ for ; i < len(data) && data[i] != '\n'; i++ {
+ }
+ if i < len(data) {
+ return i + 1, []byte(";"), nil
+ }
+ if atEOF {
+ return i, nil, nil
+ }
+ return n, nil, nil
+ case '\\':
+ if n+1 < len(data) && data[n+1] == '\n' {
+ n++
+ l.insertConcat = false
+ continue
+ }
+ fallthrough
+ default:
+ break Whitespace
+ }
+ }
+ data = data[n:]
+ if len(data) == 0 {
+ return n, nil, nil
+ }
+ switch data[0] {
+ case '$':
+ if l.insertConcat {
+ l.insertConcat = false
+ return n, []byte{}, nil
+ }
+ i := 1
+ for ; i < len(data); i++ {
+ if b := data[i]; !(b == '_' || '0' <= b && b <= '9' || 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z') {
+ break
+ }
+ }
+ if i == len(data) && !atEOF {
+ return n, nil, nil
+ }
+ if i < len(data) && data[i] == '(' {
+ i++
+ } else {
+ l.insertConcat = true
+ }
+ if i == 1 {
+ return 0, nil, fmt.Errorf(`invalid token "$"`)
+ }
+ return n + i, data[:i], nil
+ case '<', '>':
+ if len(data) == 1 && !atEOF {
+ return n, nil, nil
+ }
+ l.insertConcat = false
+ if len(data) > 1 && data[1] == '(' {
+ return n + 2, data[:2], nil
+ }
+ return n + 1, data[:1], nil
+ case '\'':
+ if l.insertConcat {
+ l.insertConcat = false
+ return n, []byte{}, nil
+ }
+ i := 1
+ for ; i < len(data); i++ {
+ if data[i] == '\'' {
+ if i+1 == len(data) && !atEOF {
+ return n, nil, nil
+ }
+ if i+1 == len(data) || data[i+1] != '\'' {
+ break
+ }
+ }
+ }
+ if i == len(data) {
+ if atEOF {
+ return 0, nil, fmt.Errorf("unterminated string")
+ }
+ return n, nil, nil
+ }
+ i++
+ l.insertConcat = true
+ return n + i, data[:i], nil
+ case '\n':
+ l.insertConcat = false
+ return n + 1, []byte(";"), nil
+ case '&':
+ if len(data) == 1 && !atEOF {
+ return n, nil, nil
+ }
+ l.insertConcat = false
+ if len(data) > 1 && data[1] == '&' {
+ return n + 2, data[:2], nil
+ }
+ return n + 1, data[:1], nil
+ case '|':
+ if len(data) == 1 && !atEOF {
+ return n, nil, nil
+ }
+ l.insertConcat = false
+ if len(data) > 1 && data[1] == '|' {
+ return n + 2, data[:2], nil
+ }
+ return n + 1, data[:1], nil
+ case ';':
+ l.insertConcat = false
+ return n + 1, data[:1], nil
+ case ')':
+ l.insertConcat = true
+ return n + 1, data[:1], nil
+ case '*':
+ if l.insertConcat {
+ l.insertConcat = false
+ return n, []byte{}, nil
+ }
+ l.insertConcat = true
+ return n + 1, data[:1], nil
+ default:
+ if l.insertConcat {
+ l.insertConcat = false
+ return n, []byte{}, nil
+ }
+ i := 1
+ Word:
+ for ; i < len(data); i++ {
+ switch data[i] {
+ case '\\':
+ if i+1 == len(data) && !atEOF {
+ return n, nil, nil
+ }
+ if i+1 < len(data) && data[i+1] == '\n' {
+ break Word
+ }
+ case '&', '|', '*', ')', ';', '$', '<', '>', '\'', '#', ' ', '\t', '\n':
+ break Word
+ }
+ }
+ if i == len(data) && !atEOF {
+ return n, nil, nil
+ }
+ l.insertConcat = true
+ return n + i, data[:i], nil
+ }
+}
+
+func splitTokens() bufio.SplitFunc {
+ return new(lexer).splitTokens
+}
+
+func main() {
+ scanner := bufio.NewScanner(os.Stdin)
+ scanner.Split(splitTokens())
+ var tokens []string
+ for scanner.Scan() {
+ tokens = append(tokens, scanner.Text())
+ }
+ if err := scanner.Err(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ fmt.Printf("%q\n", tokens)
+}