From 1e5c63770e34adeb3fff1bb9638a8a7fd7b17c14 Mon Sep 17 00:00:00 2001 From: Luxferre Date: Sat, 4 Jul 2026 10:13:40 +0300 Subject: [PATCH] added clyxc compiler --- README.md | 21 +++++++- clyxc/main.go | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 clyxc/main.go diff --git a/README.md b/README.md index 2703ef8..dbde59c 100644 --- a/README.md +++ b/README.md @@ -123,8 +123,27 @@ To run programs directly, pass the path to the `.clx` files as arguments: The Go implementation supports reading an optional ZIP archive appended directly to the end of the `clyx` binary. This allows you to package your Clyx source code into a single executable file. -To package your program, use the bundled `clyxc` utility, or: +To package your program, you can use the `clyxc` utility. +#### Installing `clyxc` +Install the utility directly from Git: +```sh +go install codeberg.org/luxferre/clyx/clyxc@latest +``` +Alternatively, if compiling locally from the repository root: +```sh +go install ./clyxc +``` + +#### Using `clyxc` +Run the compiler by passing the output binary path followed by the list of source files or directories: +```sh +clyxc out-bin-path src1 src2... +``` +*(Note: `clyxc` requires the main `clyx` interpreter binary to be installed in your `$GOPATH/bin` directory).* + +#### Manual Packaging Alternative +If you prefer not to use `clyxc`, you can package the application manually: 1. Create a ZIP archive containing all your `.clx` source files. 2. Append the ZIP archive to the compiled `clyx` binary: ```sh diff --git a/clyxc/main.go b/clyxc/main.go new file mode 100644 index 0000000..f0a5026 --- /dev/null +++ b/clyxc/main.go @@ -0,0 +1,138 @@ +package main + +import ( + "archive/zip" + "bytes" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" +) + +func getGopath() string { + gopath := os.Getenv("GOPATH") + if gopath != "" { + return gopath + } + out, err := exec.Command("go", "env", "GOPATH").Output() + if err == nil { + return strings.TrimSpace(string(out)) + } + home, err := os.UserHomeDir() + if err == nil { + return filepath.Join(home, "go") + } + return "" +} + +func addPathToZip(zw *zip.Writer, path string) error { + return filepath.Walk(path, func(walkPath string, walkInfo os.FileInfo, walkErr error) error { + if walkErr != nil { + return walkErr + } + if walkInfo.IsDir() { + return nil + } + + file, err := os.Open(walkPath) + if err != nil { + return err + } + defer file.Close() + + header, err := zip.FileInfoHeader(walkInfo) + if err != nil { + return err + } + + // Convert path to use forward slashes for ZIP compatibility + relPath := filepath.ToSlash(walkPath) + header.Name = relPath + header.Method = zip.Deflate + + writer, err := zw.CreateHeader(header) + if err != nil { + return err + } + + _, err = io.Copy(writer, file) + return err + }) +} + +func main() { + if len(os.Args) < 3 { + fmt.Println("Clyx compiler/bundler") + fmt.Println("Usage: clyxc out-bin-path src1 src2...") + os.Exit(1) + } + + outfile := os.Args[1] + srcs := os.Args[2:] + + // Check if output file exists + if _, err := os.Stat(outfile); err == nil { + fmt.Fprintf(os.Stderr, "File %s exists, won't overwrite!\n", outfile) + os.Exit(1) + } + + fmt.Println(strings.Join(srcs, " ")) + + // Locate the Clyx interpreter binary + gopath := getGopath() + if gopath == "" { + fmt.Fprintln(os.Stderr, "Error: GOPATH could not be determined") + os.Exit(1) + } + + clyxName := "clyx" + if runtime.GOOS == "windows" { + clyxName = "clyx.exe" + } + clyxPath := filepath.Join(gopath, "bin", clyxName) + + clyxBin, err := os.ReadFile(clyxPath) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: Clyx interpreter binary not found at %s. Please install it first using: go install codeberg.org/luxferre/clyx@latest\n", clyxPath) + os.Exit(1) + } + + // Create ZIP archive in memory + var zipBuf bytes.Buffer + zw := zip.NewWriter(&zipBuf) + + for _, src := range srcs { + if err := addPathToZip(zw, src); err != nil { + fmt.Fprintf(os.Stderr, "Error adding %s to ZIP: %v\n", src, err) + os.Exit(1) + } + } + + if err := zw.Close(); err != nil { + fmt.Fprintf(os.Stderr, "Error closing ZIP writer: %v\n", err) + os.Exit(1) + } + + // Write bundled executable + out, err := os.OpenFile(outfile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755) + if err != nil { + fmt.Fprintf(os.Stderr, "Error creating output file: %v\n", err) + os.Exit(1) + } + defer out.Close() + + if _, err := out.Write(clyxBin); err != nil { + fmt.Fprintf(os.Stderr, "Error writing Clyx binary: %v\n", err) + os.Exit(1) + } + + if _, err := out.Write(zipBuf.Bytes()); err != nil { + fmt.Fprintf(os.Stderr, "Error writing ZIP archive: %v\n", err) + os.Exit(1) + } + + fmt.Printf("Executable bundled into %s\n", outfile) +}