prep is a small Go tool that enables compile-time function evaluation. By using prep.Comptime, you can evaluate functions at build time, replacing them with their computed results. Just like comptime from Zig. Except it's not.
- Compile-Time Evaluation: Replace function calls with their computed results at build time.
- Simple Integration: Use
prepas both a Go library and a standalone executable. - Tooling Support: Easily integrate
prepwith your Go build process using-toolexec.
To use prep in your build process, install it as a binary executable:
go install github.com/pijng/prep/cmd/prep@latestTo use the prep library in your Go project, add it via Go modules:
go get github.com/pijng/prep
go mod tidyTo mark a function for compile-time evaluation, wrap it with prep.Comptime:
package main
import (
"fmt"
"github.com/pijng/prep"
)
func main() {
// This will be evaluated at compile-time
result := prep.Comptime(fibonacci(300))
fmt.Println("Result:", result)
}
func fibonacci(n int) int {
fmt.Printf("calculating fibonacci for %d\n", n)
if n <= 1 {
return n
}
a, b := 0, 1
for i := 2; i <= n; i++ {
a, b = b, a+b
}
return b
}After wrapping your functions with prep.Comptime, you need to use prep during the build process. This is done by using the -toolexec flag:
go build -toolexec="prep" main.goThis command will evaluate all functions wrapped with prep.Comptime and replace them with their computed results during the build.
./mainNote the speed and absence of calculating fibonacci for %d message.
Basic Literal Arguments
At it's core, prep.Comptime allows you to provide basic literals (e.g., strings, integers, floats) as arguments. Which means you can either:
- Pass a basic literal directly like this:
func job() {
prep.Comptime(myFunc(1))
}- Use a variable with the value of basic literal from the same scope as wrapped function:
func job() {
x := 1
y := 2
prep.Comptime(myFunc(x, y))
}These literals are evaluated directly at compile-time
Compile-Time Evaluation (Experimental)
Additionally, prep.Comptime also supports evaluation during compilation when you can use values from IO operations, for example:
func main() {
config := prep.Comptime(readFile("./test.txt"))
fmt.Println(config)
}
func readFile(path string) string {
content, _ := os.ReadFile(path)
return string(content)
}This way the prep will call readFile function as part of compilation step and include it's result to the final binary. This means you can remove the test.txt file after compilation, and still be able to execute the binary as if it exists.
Note that this feature is somewhat experimental and prone to errors.
Because reasons.