c65gm/internal/compiler/scriptexec.go

77 lines
1.9 KiB
Go

package compiler
import (
"bytes"
"strings"
"go.starlark.net/lib/math"
"go.starlark.net/starlark"
)
// executeScript runs a Starlark script and returns the output lines
func executeScript(scriptLines []string, ctx *CompilerContext) ([]string, error) {
// Join script lines
scriptText := strings.Join(scriptLines, "\n")
// Expand |varname| -> actual variable names
scriptText = expandVariables(scriptText, ctx)
// Wrap in function (Starlark requires control flow inside functions)
wrappedScript := "def _main():\n"
for _, line := range strings.Split(scriptText, "\n") {
wrappedScript += " " + line + "\n"
}
wrappedScript += "_main()\n"
// Capture print output
var output bytes.Buffer
thread := &starlark.Thread{
Print: func(_ *starlark.Thread, msg string) {
output.WriteString(msg)
output.WriteString("\n")
},
}
// Set execution limit (prevent infinite loops)
thread.SetMaxExecutionSteps(1000000) // 1M steps
// Predeclared functions (math module)
predeclared := starlark.StringDict{
"math": math.Module,
}
// Execute
_, err := starlark.ExecFile(thread, "script.star", wrappedScript, predeclared)
if err != nil {
return nil, err
}
// Split output into lines for assembly
outputStr := output.String()
if outputStr == "" {
return []string{}, nil
}
lines := strings.Split(strings.TrimRight(outputStr, "\n"), "\n")
return lines, nil
}
// expandVariables replaces |varname| with expanded variable names from symbol table
func expandVariables(text string, ctx *CompilerContext) string {
result := text
for {
start := strings.IndexByte(result, '|')
if start == -1 {
break
}
end := strings.IndexByte(result[start+1:], '|')
if end == -1 {
break // unclosed, let script fail
}
end += start + 1
varName := result[start+1 : end]
expandedName := ctx.SymbolTable.ExpandName(varName, ctx.CurrentScope())
result = result[:start] + expandedName + result[end+1:]
}
return result
}