main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package main import ( "bytes" "context" "fmt" "log" "os" "path/filepath" "strings" "mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/syntax" ) var ctx = context.Background() var rootDir = "test/" func main() { // TODO: this writer is responsible for the random stdout // maybe save the stdout for debug mode somehow r, err := interp.New(interp.StdIO(nil, os.Stdout, os.Stderr)) if err != nil { log.Fatal(err) } entrypoint := filepath.Join(rootDir, "main") runCommands(entrypoint, r) } // this is used to detect when the script // name changes from run to run, which allows // us to prettily-print var lastScriptPath string func runCommands(scriptPath string, r *interp.Runner) { script, err := parseFile(scriptPath) if err != nil { log.Fatal(err) } // execute every statement individually, decorating // each with ->, and doing some speshul logicks against // certain strings for _, stmt := range script.Stmts { cmdName := commandName(stmt) command, after, _ := strings.Cut(cmdName, " ") if command == "zoa-script" { // recursion detected!! :3 subScriptPath := filepath.Join(rootDir + "scripts/" + after) runCommands(subScriptPath, r) continue } // if the script name changed between runs, // print it if scriptPath != lastScriptPath { fmt.Println("\t" + scriptPath) lastScriptPath = scriptPath } fmt.Printf("\t$ %s\n", cmdName) err = r.Run(ctx, stmt) if err != nil { os.Exit(1) } } } func runCommand(c context.Context, s *syntax.Stmt, r *interp.Runner) { name := commandName(s) fmt.Printf(" -> %s\n", name) err := r.Run(c, s) if err != nil { os.Exit(1) } } func commandName(statement *syntax.Stmt) string { b := new(bytes.Buffer) syntax.NewPrinter().Print(b, statement) return b.String() } func parseFile(filename string) (*syntax.File, error) { var result = &syntax.File{} f, err := os.Open(filename) if err != nil { return result, err } result, err = syntax.NewParser().Parse(f, "") return result, err } // runStatements takes a file & runs individual // commands from that file, prepending the decorator // and returning the first error // func runScript(file *syntax.File) error { // fmt.Printf("%s%s\n", decorator, output) // return nil // }