added hsargs, hsexit and bundler mode
This commit is contained in:
@@ -120,6 +120,24 @@ To run a program directly, pass the path to the `.clx` file as an argument:
|
|||||||
./clyx path/to/program.clx
|
./clyx path/to/program.clx
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Self-contained Packaging (Compilation)
|
||||||
|
|
||||||
|
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:
|
||||||
|
1. Create a ZIP archive containing all your `.clx` source files.
|
||||||
|
2. Append the ZIP archive to the compiled `clyx` binary:
|
||||||
|
```sh
|
||||||
|
cat clyx my_app.zip > my_app_executable
|
||||||
|
chmod +x my_app_executable
|
||||||
|
```
|
||||||
|
|
||||||
|
When the packed executable runs:
|
||||||
|
- The interpreter automatically detects the appended ZIP archive.
|
||||||
|
- It iterates through all `.clx` files in the archive and evaluates (`src`) them.
|
||||||
|
- If a `_main` word is defined in the environment, it is executed automatically.
|
||||||
|
- If `_main` is not present, the interpreter falls back to running the standard REPL.
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
### Where does the name Clyx originate from?
|
### Where does the name Clyx originate from?
|
||||||
|
|||||||
@@ -313,6 +313,12 @@ func (c *Clyx) Eval(q any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Clyx) HasWord(name string) bool {
|
||||||
|
_, ok := c.words[name]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func printVal(v any) {
|
func printVal(v any) {
|
||||||
switch val := v.(type) {
|
switch val := v.(type) {
|
||||||
case int64: fmt.Printf("%d\n", val)
|
case int64: fmt.Printf("%d\n", val)
|
||||||
@@ -420,6 +426,8 @@ func NewClyx(libfname string, libCode string) *Clyx {
|
|||||||
c.words[".s"] = func() { printVal(c.reprStack()) }
|
c.words[".s"] = func() { printVal(c.reprStack()) }
|
||||||
c.words["dip"] = func() { q, y := c.pop().([]any), c.pop(); c.execQuot(q); c.stack = append(c.stack, y) }
|
c.words["dip"] = func() { q, y := c.pop().([]any), c.pop(); c.execQuot(q); c.stack = append(c.stack, y) }
|
||||||
c.words["loop"] = func() { body, cond := c.pop().([]any), c.pop().([]any); for { c.execQuot(cond); if !isTruthy(c.pop()) { break }; c.execQuot(body) } }
|
c.words["loop"] = func() { body, cond := c.pop().([]any), c.pop().([]any); for { c.execQuot(cond); if !isTruthy(c.pop()) { break }; c.execQuot(body) } }
|
||||||
|
c.words["hsargs"] = func() { c.stack = append(c.stack, toAnySlice(os.Args)) }
|
||||||
|
c.words["hsexit"] = func() { os.Exit(int(toInt(c.pop()))) }
|
||||||
c.words["hseval"] = func() { fmt.Fprintln(os.Stderr, "Error: unsupported word"); os.Exit(1) }
|
c.words["hseval"] = func() { fmt.Fprintln(os.Stderr, "Error: unsupported word"); os.Exit(1) }
|
||||||
c.words["chr"] = func() { c.stack = append(c.stack, string(rune(toInt(c.pop())))) }
|
c.words["chr"] = func() { c.stack = append(c.stack, string(rune(toInt(c.pop())))) }
|
||||||
c.words["asc"] = func() { s := c.pop().(string); r, _ := utf8.DecodeRuneInString(s); c.stack = append(c.stack, int64(r)) }
|
c.words["asc"] = func() { s := c.pop().(string); r, _ := utf8.DecodeRuneInString(s); c.stack = append(c.stack, int64(r)) }
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ class Clyx:
|
|||||||
'delf': lambda: os.remove(self._stack.pop()), 'maked': lambda: os.mkdir(self._stack.pop()), 'listd': lambda: self._stack.append(os.listdir(self._stack.pop())), 'deld': lambda: os.rmdir(self._stack.pop()),
|
'delf': lambda: os.remove(self._stack.pop()), 'maked': lambda: os.mkdir(self._stack.pop()), 'listd': lambda: self._stack.append(os.listdir(self._stack.pop())), 'deld': lambda: os.rmdir(self._stack.pop()),
|
||||||
'rand': lambda: self._stack.append(randrange(int(self._stack.pop()))), '.s': lambda: self._out_num(repr(self._stack)), 'dip': lambda: (lambda q, y: (self._exec_quot(q), self._stack.append(y)))(self._stack.pop(), self._stack.pop()),
|
'rand': lambda: self._stack.append(randrange(int(self._stack.pop()))), '.s': lambda: self._out_num(repr(self._stack)), 'dip': lambda: (lambda q, y: (self._exec_quot(q), self._stack.append(y)))(self._stack.pop(), self._stack.pop()),
|
||||||
'loop': lambda: (lambda body, cond: self._exec_loop(cond, body))(self._stack.pop(), self._stack.pop()),
|
'loop': lambda: (lambda body, cond: self._exec_loop(cond, body))(self._stack.pop(), self._stack.pop()),
|
||||||
|
'hsargs': lambda: self._stack.append(list(sys.argv)),
|
||||||
|
'hsexit': lambda: sys.exit(int(self._stack.pop())),
|
||||||
'hseval': lambda: self._stack.append(eval(self._stack.pop())),
|
'hseval': lambda: self._stack.append(eval(self._stack.pop())),
|
||||||
'chr': lambda: self._stack.append(chr(int(self._stack.pop()))), 'asc': lambda: self._stack.append(ord(self._stack.pop())),
|
'chr': lambda: self._stack.append(chr(int(self._stack.pop()))), 'asc': lambda: self._stack.append(ord(self._stack.pop())),
|
||||||
'sin': lambda: self._stack.append(math.sin(float(self._stack.pop()))), 'cos': lambda: self._stack.append(math.cos(float(self._stack.pop()))), 'atan': lambda: self._stack.append(math.atan(float(self._stack.pop()))),
|
'sin': lambda: self._stack.append(math.sin(float(self._stack.pop()))), 'cos': lambda: self._stack.append(math.cos(float(self._stack.pop()))), 'atan': lambda: self._stack.append(math.atan(float(self._stack.pop()))),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "clyx"
|
name = "clyx"
|
||||||
version = "0.1.1"
|
version = "0.2"
|
||||||
description = "Clyx programming language interpreter"
|
description = "Clyx programming language interpreter"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.7"
|
requires-python = ">=3.7"
|
||||||
|
|||||||
@@ -282,6 +282,14 @@ Any line fragment starting with `#` to the end of the physical source line is re
|
|||||||
- **Stack Effect**: `( -- )`
|
- **Stack Effect**: `( -- )`
|
||||||
- **Description**: Prints the current stack representation to standard output (useful for debugging).
|
- **Description**: Prints the current stack representation to standard output (useful for debugging).
|
||||||
|
|
||||||
|
### `hsargs` (Host Arguments)
|
||||||
|
- **Stack Effect**: `( -- [args] )`
|
||||||
|
- **Description**: Pushes a list/quotation of all passed command line arguments onto the stack.
|
||||||
|
|
||||||
|
### `hsexit` (Host Exit)
|
||||||
|
- **Stack Effect**: `( code -- )`
|
||||||
|
- **Description**: Exits the program/interpreter with the specified exit code.
|
||||||
|
|
||||||
### `hseval` (Host Language Evaluation)
|
### `hseval` (Host Language Evaluation)
|
||||||
- **Stack Effect**: `( expr -- val )`
|
- **Stack Effect**: `( expr -- val )`
|
||||||
- **Description**: Pops the string `expr` from the stack, evaluates it as an expression in the host language (e.g., Python), and pushes the resulting value back onto the stack.
|
- **Description**: Pops the string `expr` from the stack, evaluates it as an expression in the host language (e.g., Python), and pushes the resulting value back onto the stack.
|
||||||
|
|||||||
@@ -4,9 +4,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/zip"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
clyx "codeberg.org/luxferre/clyx/clyx-go"
|
clyx "codeberg.org/luxferre/clyx/clyx-go"
|
||||||
)
|
)
|
||||||
@@ -17,7 +20,73 @@ var defaultLibCode string
|
|||||||
//go:embed clyx_repl.clx
|
//go:embed clyx_repl.clx
|
||||||
var defaultReplCode string
|
var defaultReplCode string
|
||||||
|
|
||||||
|
func runZipArchive(zr *zip.Reader) error {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %v\n", r)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
c := clyx.NewClyx("", defaultLibCode)
|
||||||
|
|
||||||
|
for _, file := range zr.File {
|
||||||
|
if !file.FileInfo().IsDir() && strings.HasSuffix(strings.ToLower(file.Name), ".clx") {
|
||||||
|
rc, err := file.Open()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error opening %s in archive: %w", file.Name, err)
|
||||||
|
}
|
||||||
|
data, err := io.ReadAll(rc)
|
||||||
|
rc.Close()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading %s in archive: %w", file.Name, err)
|
||||||
|
}
|
||||||
|
c.Run(string(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.HasWord("_main") {
|
||||||
|
c.Run("_main")
|
||||||
|
} else {
|
||||||
|
c.Run(defaultReplCode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectAndRunZip() bool {
|
||||||
|
exePath, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
f, err := os.Open(exePath)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
info, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
zr, err := zip.NewReader(f, info.Size())
|
||||||
|
if err != nil {
|
||||||
|
return false // No ZIP appended
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZIP found! Run it
|
||||||
|
if err := runZipArchive(zr); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
if detectAndRunZip() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if len(os.Args) > 1 {
|
if len(os.Args) > 1 {
|
||||||
clyx.RunFile(os.Args[1], defaultLibCode)
|
clyx.RunFile(os.Args[1], defaultLibCode)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -215,3 +215,13 @@ my_inline 100 = if [ " :: (inline) PASS" puts cr ] [ " :: (inline) FAIL" puts
|
|||||||
# " hseval FAIL" puts cr
|
# " hseval FAIL" puts cr
|
||||||
# ]
|
# ]
|
||||||
|
|
||||||
|
# 12. Host Arguments & Host Exit
|
||||||
|
"Testing host arguments and exit..." puts cr
|
||||||
|
hsargs isquot [ hsargs qlen 0 > ] and if [
|
||||||
|
" hsargs PASS" puts cr
|
||||||
|
] [
|
||||||
|
" hsargs FAIL" puts cr
|
||||||
|
]
|
||||||
|
|
||||||
|
0 hsexit
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user