From 46ff217e141c4cfa43f61e5db40b026c6754621d Mon Sep 17 00:00:00 2001 From: Luxferre Date: Sat, 4 Jul 2026 16:56:08 +0300 Subject: [PATCH] q-form renaming effort --- README.md | 9 ++-- clyx-go/clyx.go | 56 ++++++++++++------------- clyx-python/clyx/clyx.py | 26 ++++++------ clyx_manual.md | 88 ++++++++++++++++++++-------------------- lib.clx | 2 +- test.clx | 12 +++--- 6 files changed, 96 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index 65a6146..8a9e4e5 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,10 @@ The core spec of Clyx is mostly stable but the standard library is still a work- ## Language features -- Single, unbounded data stack that can hold numbers, strings and quotations (lists) +- Single, unbounded data stack that can hold numbers, strings and lists (here called Q-forms) - Forth-like parsing, RPN (postfix notation) and word definition mechanics -- Tcl-like quotation (list) bracket syntax -- Janet-like quotation (list) operations -- Homoiconicity: quotations and strings can be manipulated as data and executed dynamically +- Tcl-like bracket syntax and Janet-like list operations on Q-forms +- Homoiconicity: Q-forms and strings can be manipulated as data and executed dynamically - Character I/O (implementation-dependent) - File I/O (may not support all features on some targets) - TCP socket I/O (may not be supported on some targets) @@ -175,7 +174,7 @@ Additionally, this name can be viewed as an acronym hinting at the order some bi More of a high-level one (because you don't have any memory control), but in fact it can be both. -Clyx combines the conceptual simplicity of Forth (and concatenative/RPN programming in general) with list definition syntax, list manipulation and metaprogramming capabilities of Janet and Tcl, making it much more practical for everyday tasks. At the same time, you can build abstraction levels as high as you need to, making it a nice choice for creating custom DSLs (domain-specific languages), especially with the `next` word that pushes the next logical token onto the stack, allowing to change token processing order. +Clyx combines the conceptual simplicity of Forth (and concatenative/RPN programming in general) with list definition syntax, manipulation and metaprogramming capabilities of Janet and Tcl, making it much more practical for everyday tasks. At the same time, you can build abstraction levels as high as you need to, making it a nice choice for creating custom DSLs (domain-specific languages), especially with the `next` word that pushes the next logical token onto the stack, allowing to change token processing order. ### Why not just pick up a Forth instead? diff --git a/clyx-go/clyx.go b/clyx-go/clyx.go index 2dd249f..8577eea 100644 --- a/clyx-go/clyx.go +++ b/clyx-go/clyx.go @@ -182,19 +182,19 @@ func normToken(t string) any { return t } -func parseQuot(tokens *[]any) ([]any, error) { - var quot []any +func parseQForm(tokens *[]any) ([]any, error) { + var qform []any for { if len(*tokens) == 0 { return nil, fmt.Errorf("Syntax error: unbalanced brackets") } t := (*tokens)[0] *tokens = (*tokens)[1:] - if s, ok := t.(string); ok && s == "]" { return quot, nil } + if s, ok := t.(string); ok && s == "]" { return qform, nil } if s, ok := t.(string); ok && s == "[" { - sub, err := parseQuot(tokens) + sub, err := parseQForm(tokens) if err != nil { return nil, err } - quot = append(quot, sub) + qform = append(qform, sub) } else { - if s, ok := t.(string); ok { quot = append(quot, normToken(s)) } else { quot = append(quot, t) } + if s, ok := t.(string); ok { qform = append(qform, normToken(s)) } else { qform = append(qform, t) } } } } @@ -225,17 +225,17 @@ func (c *Clyx) reprStack() string { return "[" + strings.Join(parts, ", ") + "]" } -func (c *Clyx) execQuot(q []any) { +func (c *Clyx) execQForm(q []any) { c.tokenStreams = append(c.tokenStreams, c.tokens) c.tokens = make([]any, len(q)) copy(c.tokens, q) for len(c.tokens) > 0 { t := c.tokens[0] c.tokens = c.tokens[1:] - if listVal, ok := t.([]any); ok { - c.stack = append(c.stack, listVal) + if qformVal, ok := t.([]any); ok { + c.stack = append(c.stack, qformVal) } else if s, ok := t.(string); ok && s == "[" { - qParsed, err := parseQuot(&c.tokens) + qParsed, err := parseQForm(&c.tokens) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err); break } c.stack = append(c.stack, qParsed) } else { @@ -247,7 +247,7 @@ func (c *Clyx) execQuot(q []any) { if isWord { switch val := v.(type) { case func(): val() - case []any: c.execQuot(val) + case []any: c.execQForm(val) default: c.stack = append(c.stack, val) } } else { @@ -266,7 +266,7 @@ func (c *Clyx) popCallerToken() any { t := (*stream)[0] *stream = (*stream)[1:] if s, ok := t.(string); ok && s == "[" { - qParsed, err := parseQuot(stream) + qParsed, err := parseQForm(stream) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err); return nil } return qParsed } @@ -279,9 +279,9 @@ func (c *Clyx) cmdDefw() { var val any if len(*stream) > 0 { t0 := (*stream)[0] - isQuot := false - if s, ok := t0.(string); ok && s == "[" { isQuot = true } else if _, ok := t0.([]any); ok { isQuot = true } - if isQuot { val = c.popCallerToken() } + isQForm := false + if s, ok := t0.(string); ok && s == "[" { isQForm = true } else if _, ok := t0.([]any); ok { isQForm = true } + if isQForm { val = c.popCallerToken() } } if val == nil { val = c.pop() } name := c.pop().(string) @@ -299,7 +299,7 @@ func (c *Clyx) cmdType() { } func (c *Clyx) Run(code string) { - c.execQuot(toAnySlice(tokenize(code))) + c.execQForm(toAnySlice(tokenize(code))) } func (c *Clyx) Eval(q any) { @@ -307,11 +307,11 @@ func (c *Clyx) Eval(q any) { if r := recover(); r != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", r) } }() if s, ok := q.(string); ok { - c.execQuot(toAnySlice(tokenize(s))) + c.execQForm(toAnySlice(tokenize(s))) } else if slice, ok := q.([]any); ok { - c.execQuot(slice) + c.execQForm(slice) } else { - panic("eval expects string/quotation") + panic("eval expects string or Q-form") } } @@ -345,7 +345,7 @@ func NewClyx(libfname string, libCode string) *Clyx { cVal, tVal, fVal := c.pop(), c.pop(), c.pop() runVal := fVal if isTruthy(cVal) { runVal = tVal } - if listVal, ok := runVal.([]any); ok { c.execQuot(listVal) } else { c.execQuot([]any{runVal}) } + if qformVal, ok := runVal.([]any); ok { c.execQForm(qformVal) } else { c.execQForm([]any{runVal}) } } c.words["cons"] = func() { q, x := c.pop().([]any), c.pop(); c.stack = append(c.stack, append([]any{x}, q...)) } c.words["uncons"] = func() { @@ -420,16 +420,16 @@ func NewClyx(libfname string, libCode string) *Clyx { c.words["write"] = func() { data, fd := c.pop(), c.pop() var toWrite string - if listVal, ok := data.([]any); ok { + if qformVal, ok := data.([]any); ok { var sb strings.Builder - for _, val := range listVal { + for _, val := range qformVal { sb.WriteByte(byte(toInt(val))) } toWrite = sb.String() } else if strVal, ok := data.(string); ok { toWrite = strVal } else { - panic("write expects string or list") + panic("write expects string or Q-form") } n, err := fd.(ClyxFile).Write(toWrite) if err != nil { @@ -450,11 +450,11 @@ func NewClyx(libfname string, libCode string) *Clyx { if err != nil { panic(err) } - var list []any + var qform []any for i := 0; i < len(res); i++ { - list = append(list, int64(res[i])) + qform = append(qform, int64(res[i])) } - c.stack = append(c.stack, list) + c.stack = append(c.stack, qform) } } c.words["close"] = func() { if fd, ok := c.pop().(ClyxFile); ok { fd.Close() } } @@ -464,8 +464,8 @@ func NewClyx(libfname string, libCode string) *Clyx { c.words["deld"] = func() { if err := os.Remove(c.pop().(string)); err != nil { panic(err) } } c.words["rand"] = func() { limit := toInt(c.pop()); if limit <= 0 { c.stack = append(c.stack, int64(0)) } else { c.stack = append(c.stack, rand.Int63n(limit)) } } 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["loop"] = func() { body, cond := c.pop().([]any), c.pop().([]any); for { c.execQuot(cond); if !isTruthy(c.pop()) { break }; c.execQuot(body) } } + c.words["dip"] = func() { q, y := c.pop().([]any), c.pop(); c.execQForm(q); c.stack = append(c.stack, y) } + c.words["loop"] = func() { body, cond := c.pop().([]any), c.pop().([]any); for { c.execQForm(cond); if !isTruthy(c.pop()) { break }; c.execQForm(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["l2s"] = func() { diff --git a/clyx-python/clyx/clyx.py b/clyx-python/clyx/clyx.py index 9e3d780..1af1b7f 100755 --- a/clyx-python/clyx/clyx.py +++ b/clyx-python/clyx/clyx.py @@ -51,7 +51,7 @@ class Clyx: self._stack, self._tokens, self._token_streams = [], [], [] self._words = { 'dup': lambda: self._stack.append(self._stack[-1]), 'drop': lambda: self._stack.pop(), 'swap': lambda: self._stack.extend([self._stack.pop(), self._stack.pop()]), 'i': lambda: self._eval(self._stack.pop()), - 'cond': lambda: (lambda c, t, f: self._exec_quot(t if c else f))(self._stack.pop(), self._stack.pop(), self._stack.pop()), 'cons': lambda: (lambda q: self._stack.append([self._stack.pop()] + q))(self._stack.pop()), + 'cond': lambda: (lambda c, t, f: self._exec_qform(t if c else f))(self._stack.pop(), self._stack.pop(), self._stack.pop()), 'cons': lambda: (lambda q: self._stack.append([self._stack.pop()] + q))(self._stack.pop()), 'uncons': lambda: (lambda q: (lambda l: self._stack.extend([l, 0] if not l else [l[1:], l[0], 1]))(list(map(ord, q)) if isinstance(q, str) else q))(self._stack.pop()), 'qpop': lambda: (lambda q: self._stack.extend([q[:-1], q[-1]]))(self._stack.pop()), 'qlen': lambda: (lambda v: self._stack.append(len(v) if isinstance(v, list) else 0))(self._stack.pop()), 'qget': lambda: (lambda i,v: self._stack.append(v[i] if isinstance(v, list) else None))(self._stack.pop(), self._stack.pop()), 'qset': self._cmd_qset, 'slen': lambda: (lambda v: self._stack.append(len(v) if isinstance(v, str) else 0))(self._stack.pop()), @@ -65,7 +65,7 @@ class Clyx: 'read': self._cmd_read, 'close': lambda: (lambda fd: fd.close() if not (fd in (sys.stdin, sys.stdout, sys.stderr) or fd.__class__.__name__ == 'FD0') else None)(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_qform(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()), 'hsargs': lambda: self._stack.append(list(sys.argv)), 'hsexit': lambda: sys.exit(int(self._stack.pop())), @@ -102,8 +102,8 @@ class Clyx: self._stack.append(res) def _cmd_qset(self): i, v, x = self._stack.pop(), self._stack.pop(), self._stack.pop(); (v.pop(int(i)), v.insert(int(i), x)) if isinstance(v, list) else None; self._stack.append(v if isinstance(v, list) else None) def _exec_loop(self, c, b): - while (self._exec_quot(c), self._stack.pop())[1]: self._exec_quot(b) - _pop_caller_token = lambda self: (lambda t: (t.pop(0) if t[0] != '[' else self._parse_quot((t.pop(0), t)[1])) if t else None)(self._token_streams[-1] if self._token_streams else self._tokens) + while (self._exec_qform(c), self._stack.pop())[1]: self._exec_qform(b) + _pop_caller_token = lambda self: (lambda t: (t.pop(0) if t[0] != '[' else self._parse_qform((t.pop(0), t)[1])) if t else None)(self._token_streams[-1] if self._token_streams else self._tokens) def _cmd_defw(self): t = self._token_streams[-1] if self._token_streams else self._tokens; val = self._pop_caller_token() if (t and (t[0] == '[' or isinstance(t[0], list))) else self._stack.pop(); self._words[self._stack.pop()] = val def _tokenize(self, code): @@ -116,13 +116,13 @@ class Clyx: _next_token = lambda self: self._tokens.pop(0) if self._tokens else None - def _parse_quot(self, tokens=None): - tokens, quot = self._tokens if tokens is None else tokens, [] + def _parse_qform(self, tokens=None): + tokens, qform = self._tokens if tokens is None else tokens, [] try: while True: t = tokens.pop(0) - if t == ']': return quot - quot.append(self._parse_quot(tokens) if t == '[' else self._norm_token(t)) + if t == ']': return qform + qform.append(self._parse_qform(tokens) if t == '[' else self._norm_token(t)) except IndexError: raise SyntaxError('Syntax error: unbalanced brackets') def _norm_token(self, t): @@ -130,25 +130,25 @@ class Clyx: except: return t[1:-1].replace('\\\\', '\x01').replace('\\n', '\n').replace('\\t', '\t').replace('\\r', '\r').replace('\\s', ' ').replace('\\b', '\b').replace('\\f', '\f').replace('\\"', '"').replace('\x01', '\\') if (t.startswith('"') and t.endswith('"')) else t _flush = lambda self, fd: getattr(fd, 'flush', lambda: None)() - def _exec_quot(self, q): + def _exec_qform(self, q): self._token_streams.append(self._tokens); self._tokens = list(q) while self._tokens: t = self._next_token() if isinstance(t, list): self._stack.append(t) - elif t == '[': self._stack.append(self._parse_quot(self._tokens)) + elif t == '[': self._stack.append(self._parse_qform(self._tokens)) else: t = self._norm_token(t) if isinstance(t, str) else t v = self._words.get(t) if isinstance(t, str) else None if callable(v): v() - elif isinstance(v, list): self._exec_quot(v) + elif isinstance(v, list): self._exec_qform(v) elif v is not None: self._stack.append(v) else: self._stack.append(t) self._tokens = self._token_streams.pop() def _eval(self, q): - try: self._exec_quot(self._tokenize(q) if isinstance(q, str) else q) + try: self._exec_qform(self._tokenize(q) if isinstance(q, str) else q) except Exception as e: sys.stderr.write(f'Error: {e}\n') - run = lambda self, code: self._exec_quot(self._tokenize(code)) + run = lambda self, code: self._exec_qform(self._tokenize(code)) def clyx(f): try: diff --git a/clyx_manual.md b/clyx_manual.md index e3d62a2..258d722 100644 --- a/clyx_manual.md +++ b/clyx_manual.md @@ -1,6 +1,6 @@ # Clyx Reference Manual -Clyx is a concatenative, stack-oriented programming language designed for simplicity, minimalism, and metaprogramming capabilities. It uses postfix notation (aka Reverse Polish Notation, RPN), where operations are performed on a global stack. Clyx features homoiconicity: code is structured as quotations (lists), which can be manipulated as data and executed dynamically. +Clyx is a concatenative, stack-oriented programming language designed for simplicity, minimalism, and metaprogramming capabilities. It uses postfix notation (aka Reverse Polish Notation, RPN), where operations are performed on a global stack. Clyx features homoiconicity: code is structured as Q-forms (special forms to represent data similar to Python or Tcl lists), which can be manipulated as data and executed dynamically. This document describes the stack effects and behavior of the **50 primitive core words**, **3 bootstrapped core words**, and **42 standard library words** present in the reference implementation. @@ -25,8 +25,8 @@ Stack effects are described in the format: ``` Where: - The rightmost element is the top of the stack. -- `x`, `y`, `z` represent values (numbers, strings, or quotations). -- `[q]` represents a quotation (a block of code enclosed in brackets, e.g., `[ 1 + ]`). +- `x`, `y`, `z` represent values (numbers, strings, or Q-forms). +- `[q]` represents a Q-form (a block of code or data enclosed in brackets, e.g., `[ 1 + ]`). - `s` represents a string. - `c` represents a boolean flag (`1` for true, `0` for false). @@ -37,7 +37,7 @@ Where: Clyx supports three basic data types: 1. **Numbers**: Integers (e.g., `42`) and Floating-point numbers (e.g., `3.14`). 2. **Strings**: Sequences of characters enclosed in double quotes. Escaping double quotes (`\"`), backslashes (`\\`), and whitespace characters (`\n` for newline, `\t` for tab, `\r` for carriage return, `\s` for space, `\b` for backspace, `\f` for form feed) is supported inside strings using a backslash (e.g., `"hello \"world\""`, `"a\\b"`, or `"hello\nworld"`). -3. **Quotations**: Ordered lists of values or code blocks enclosed in square brackets (e.g., `[ 1 2 + ]`). +3. **Q-forms** (short for _quotation forms_): Ordered lists of values or code blocks enclosed in square brackets (e.g., `[ 1 2 + ]`). ### Word Name Collision Rule In Clyx, the evaluation engine executes any string token that matches a defined word name. This means that double-quoted string literals that match a registered word name (such as `"dup"`, `"+"`, or `"."`) **will be executed as that word** when evaluated, rather than being pushed as a literal string. @@ -75,31 +75,31 @@ Any line fragment starting with `#` to the end of the physical source line is re --- -## 2. Quotation Operations (Lists) +## 2. Q-form Operations ### `cons` (CONStruct) - **Stack Effect**: `( x [q] -- [x q...] )` -- **Description**: Prepends the element `x` to the beginning of the quotation/list `[q]`. +- **Description**: Prepends the element `x` to the beginning of the Q-form `[q]`. ### `uncons` (UNCONStruct) - **Stack Effect**: `( [q] -- [q_rest] x 1 )` or `( [] -- [] 0 )` -- **Description**: Deconstructs a quotation or string. If it is non-empty, it pushes the remaining list/string, the first element/character code, and a success flag `1`. If it is empty, it pushes the empty list/string and a failure flag `0`. +- **Description**: Deconstructs a Q-form or string. If it is non-empty, it pushes the remaining Q-form/string, the first element/character code, and a success flag `1`. If it is empty, it pushes the empty Q-form/string and a failure flag `0`. -### `qpop` (Quotation Pop) +### `qpop` (Q-form Pop) - **Stack Effect**: `( [q] -- [q_rest] x )` -- **Description**: Pops the last element `x` from the quotation/list `[q]`, returning the remaining list and the popped element. +- **Description**: Pops the last element `x` from the Q-form `[q]`, returning the remaining Q-form and the popped element. -### `qlen` (Quotation Length) +### `qlen` (Q-form Length) - **Stack Effect**: `( [q] -- len )` -- **Description**: Pushes the length of the list `[q]`. Pushes `0` if the operand is not a list. +- **Description**: Pushes the length of the Q-form `[q]`. Pushes `0` if the operand is not a Q-form. -### `qget` (Quotation Get) +### `qget` (Q-form Get) - **Stack Effect**: `( [q] idx -- x )` -- **Description**: Pushes the element at index `idx` from the list `[q]`. Pushes `None` if the operand is not a list. +- **Description**: Pushes the element at index `idx` from the Q-form `[q]`. Pushes `None` if the operand is not a Q-form. -### `qset` (Quotation Set) +### `qset` (Q-form Set) - **Stack Effect**: `( x [q] idx -- [q_new] )` -- **Description**: Sets the element at index `idx` in the list `[q]` to the value `x`, returning the modified list. Pushes `None` if the operand is not a list. +- **Description**: Sets the element at index `idx` in the Q-form `[q]` to the value `x`, returning the modified Q-form. Pushes `None` if the operand is not a Q-form. --- @@ -117,13 +117,13 @@ Any line fragment starting with `#` to the end of the physical source line is re - **Stack Effect**: `( s -- code )` - **Description**: Converts the first character of the string `s` to its integer ASCII character code. -### `s2l` (String to List) +### `s2l` (String to Q-form) - **Stack Effect**: `( val -- [q] )` -- **Description**: Converts the value `val` (if it is a string) to a list of its byte values/integers. If `val` is already a list, it does nothing. +- **Description**: Converts the value `val` (if it is a string) to a Q-form of its byte values/integers. If `val` is already a Q-form, it does nothing. -### `l2s` (List to String) +### `l2s` (Q-form to String) - **Stack Effect**: `( [q] -- s )` -- **Description**: Converts the list of integers `[q]` (character/byte codes) into its string representation `s`. Reverts the operation of `s2l`. +- **Description**: Converts the Q-form of integers `[q]` (character/byte codes) into its string representation `s`. Reverts the operation of `s2l`. --- @@ -195,15 +195,15 @@ Any line fragment starting with `#` to the end of the physical source line is re ### `i` (Interpret) - **Stack Effect**: `( [q] -- )` -- **Description**: "De-quotes" and immediately executes the quotation `[q]`. +- **Description**: "De-quotes" and immediately executes the Q-form `[q]`. ### `cond` - **Stack Effect**: `( f t c -- )` -- **Description**: Pops the condition `c`, the true branch quotation `t`, and the false branch quotation `f`. If `c` is non-zero, executes `t`; otherwise executes `f`. +- **Description**: Pops the condition `c`, the true branch Q-form `t`, and the false branch Q-form `f`. If `c` is non-zero, executes `t`; otherwise executes `f`. ### `loop` - **Stack Effect**: `( [cond] [body] -- )` -- **Description**: Repeatedly executes the condition quotation `[cond]`. If the top element of the stack after `[cond]` is non-zero, executes `[body]` and loops again; otherwise terminates. +- **Description**: Repeatedly executes the condition Q-form `[cond]`. If the top element of the stack after `[cond]` is non-zero, executes `[body]` and loops again; otherwise terminates. ### `dip` (Stack-dip) - **Stack Effect**: `( y [q] -- y )` @@ -215,7 +215,7 @@ Any line fragment starting with `#` to the end of the physical source line is re ### `defw` (Define Word) - **Stack Effect**: `( name [q] -- )` or `( name x -- )` -- **Description**: Defines a new word associated with `name`. If the next token in the interpreter stream is `[`, parses it as a quotation and defines `name` to execute that quotation. Otherwise, defines `name` to represent the top stack value `x`. +- **Description**: Defines a new word associated with `name`. If the next token in the interpreter stream is `[`, parses it as a Q-form and defines `name` to execute that Q-form. Otherwise, defines `name` to represent the top stack value `x`. ### `next` (Next Token) - **Stack Effect**: `( -- token )` @@ -223,7 +223,7 @@ Any line fragment starting with `#` to the end of the physical source line is re ### `type` - **Stack Effect**: `( x -- x type_code )` -- **Description**: Inspects the top element of the stack `x` (without popping it) and pushes its type code: `0` if it's a number, `1` if it's a string, or `2` if it's a list (quotation). +- **Description**: Inspects the top element of the stack `x` (without popping it) and pushes its type code: `0` if it's a number, `1` if it's a string, or `2` if it's a Q-form. --- @@ -277,7 +277,7 @@ Any line fragment starting with `#` to the end of the physical source line is re ### `listd` (List Directory) - **Stack Effect**: `( path -- [contents] )` -- **Description**: Lists the contents of the directory at `path`. Pushes a quotation containing a list of strings representing the names of the files and directories inside the directory. +- **Description**: Lists the contents of the directory at `path`. Pushes a Q-form of strings representing the names of the files and directories inside the directory. **N.B.**: this primitive may not be supported on all targets. @@ -303,9 +303,9 @@ Any line fragment starting with `#` to the end of the physical source line is re ### `hsargs` (Host Arguments) - **Stack Effect**: `( -- [args] )` -- **Description**: Pushes a list/quotation of all passed command line arguments onto the stack. +- **Description**: Pushes a Q-form of all passed command line arguments onto the stack. -**N.B.**: on the platforms that don't support CLI arguments, this primitive will always push an empty quotation. +**N.B.**: on the platforms that don't support CLI arguments, this primitive will always push an empty Q-form. ### `hsexit` (Host Exit) - **Stack Effect**: `( code -- )` @@ -333,11 +333,11 @@ These words are defined in Clyx itself during the interpreter's bootstrap phase: ### `readf` (Read File) - **Stack Effect**: `( filename -- [content] )` -- **Description**: Reads the entire contents of the file `filename` and pushes it as a list of bytes. +- **Description**: Reads the entire contents of the file `filename` and pushes it as a Q-form of byte values. ### `src` (Source) - **Stack Effect**: `( filename -- )` -- **Description**: Reads the file `filename` using `readf`, converts the list of bytes to a string using `l2s`, and runs the resulting Clyx code in real-time using `i`. +- **Description**: Reads the file `filename` using `readf`, converts the Q-form of bytes to a string using `l2s`, and runs the resulting Clyx code in real-time using `i`. --- @@ -367,12 +367,12 @@ These words are defined in the standard library file `lib.clx` and loaded dynami #### `if` - **Syntax**: `if [true_actions] [false_actions]` -- **Stack Effect**: `( c -- )` (pops condition from stack, parses branch quotations from the execution stream). +- **Stack Effect**: `( c -- )` (pops condition from stack, parses branch Q-forms from the execution stream). - **Description**: Executes `true_actions` if `c` is non-zero, otherwise executes `false_actions`. #### `while` - **Syntax**: `while [actions]` -- **Stack Effect**: `( -- )` (parses body quotation from the execution stream). +- **Stack Effect**: `( -- )` (parses body Q-form from the execution stream). - **Description**: Loops repeatedly. The body `actions` is executed, and must leave the next condition value on the stack. If the condition is non-zero, the loop repeats; otherwise it terminates. ### 10.3 Mathematics & Arithmetic @@ -435,7 +435,7 @@ These words are defined in the standard library file `lib.clx` and loaded dynami #### `puts` (Put String) - **Stack Effect**: `( s -- )` or `( [q] -- )` -- **Description**: Outputs a string or list of character codes to standard output character-by-character. +- **Description**: Outputs a string or Q-form of character codes to standard output character-by-character. #### `readln` (Read Line) - **Stack Effect**: `( -- s )` @@ -461,9 +461,9 @@ These words are defined in the standard library file `lib.clx` and loaded dynami - **Stack Effect**: `( x -- x c )` - **Description**: Pushes `1` if the top stack element `x` is a string; otherwise pushes `0`. Does not pop `x`. -#### `isquot` (Is Quotation) +#### `isq` (Is Q-form) - **Stack Effect**: `( x -- x c )` -- **Description**: Pushes `1` if the top stack element `x` is a list (quotation); otherwise pushes `0`. Does not pop `x`. +- **Description**: Pushes `1` if the top stack element `x` is a Q-form; otherwise pushes `0`. Does not pop `x`. #### `=` (Equal) - **Stack Effect**: `( a b -- c )` @@ -490,13 +490,13 @@ These words are defined in the standard library file `lib.clx` and loaded dynami - **Description**: Pops `b`, pops `a`, and pushes `1` if `a` is less than or equal to `b`; otherwise pushes `0`. #### `vlen` (Value Length) - **Stack Effect**: `( val -- len )` -- **Description**: Pushes the length of `val`. If `val` is a list, it returns the number of elements; if it is a string, it returns the number of characters. +- **Description**: Pushes the length of `val`. If `val` is a Q-form, it returns the number of elements; if it is a string, it returns the number of characters. ### 10.7 String Operations #### `streq` (String Equal) - **Stack Effect**: `( s1 s2 -- c )` -- **Description**: Pops `s2`, pops `s1`, and pushes `1` if the two strings `s1` and `s2` (or their character list equivalents) are equal; otherwise pushes `0`. +- **Description**: Pops `s2`, pops `s1`, and pushes `1` if the two strings `s1` and `s2` (or their character Q-form equivalents) are equal; otherwise pushes `0`. #### `lower` (To Lowercase) - **Stack Effect**: `( s -- s_lower )` @@ -506,23 +506,23 @@ These words are defined in the standard library file `lib.clx` and loaded dynami - **Stack Effect**: `( s -- s_upper )` - **Description**: Pops the string `s` and pushes its uppercase equivalent `s_upper` onto the stack. -#### `s+` (String / List Concatenation) +#### `s+` (String / Q-form Concatenation) - **Stack Effect**: `( s1 s2 -- [s1_s2] )` -- **Description**: Concatenates `s1` and `s2` (which can be strings or lists of character codes) and pushes the result as a list of character codes representing the concatenated string. +- **Description**: Concatenates `s1` and `s2` (which can be strings or Q-forms of character codes) and pushes the result as a Q-form of character codes representing the concatenated string. -### 10.8 List Operations +### 10.8 Q-form Operations -#### `qpush` (Quotation Push) +#### `qpush` (Q-form Push) - **Stack Effect**: `( [q] x -- [q... x] )` -- **Description**: Appends the element `x` to the end of the quotation/list `[q]`. +- **Description**: Appends the element `x` to the end of the Q-form `[q]`. #### `rev` - **Stack Effect**: `( [q] -- [q_rev] )` -- **Description**: Reverses the order of elements in the list `[q]`. +- **Description**: Reverses the order of elements in the Q-form `[q]`. -#### `lcat` (List Concatenation) +#### `lcat` (Q-form Concatenation) - **Stack Effect**: `( [q1] [q2] -- [q1_q2] )` -- **Description**: Concatenates two lists `[q1]` and `[q2]`. +- **Description**: Concatenates two Q-forms `[q1]` and `[q2]`. ### 10.9 Bitwise Operations diff --git a/lib.clx b/lib.clx index 6f9f38a..2bfc6af 100644 --- a/lib.clx +++ b/lib.clx @@ -33,7 +33,7 @@ :: <= [ > not ] :: isnum [ type 0= ] :: isstr [ type 1 = ] -:: isquot [ type 2 = ] +:: isq [ type 2 = ] :: vlen [ type 2 = if [ qlen ] [ slen ] ] :: s2l [ type 2 = if [] [ dup slen 0 = if [ drop [] ] [ uncons drop swap cons ] ] ] :: asc [ s2l 0 qget ] diff --git a/test.clx b/test.clx index 6339427..7637897 100644 --- a/test.clx +++ b/test.clx @@ -99,10 +99,10 @@ readln puts cr # Test readln ] [ drop drop drop " isstr FAIL" puts cr ] -[ 1 2 ] isquot [ 42 isquot not ] dip and [ "hello" isquot not ] dip and if [ - drop drop drop " isquot PASS" puts cr +[ 1 2 ] isq [ 42 isq not ] dip and [ "hello" isq not ] dip and if [ + drop drop drop " isq PASS" puts cr ] [ - drop drop drop " isquot FAIL" puts cr + drop drop drop " isq FAIL" puts cr ] "Testing binary predicates..." puts cr 5 3 > @@ -171,8 +171,8 @@ if [ " l2s, streq, lower, and upper FAIL" puts cr ] -# 8. List Operations -"Testing list operations..." puts cr +# 8. Q-form Operations +"Testing Q-form operations..." puts cr 5 [ 6 ] cons uncons 1 = [ 5 = ] dip and [ [ 6 ] streq ] dip and if [ " cons/uncons PASS" puts cr ] [ " cons/uncons FAIL" puts cr ] # Test cons/uncons 5 [ 6 ] qpush [ 6 5 ] streq if [ " qpush PASS" puts cr ] [ " qpush FAIL" puts cr ] # Test qpush @@ -222,7 +222,7 @@ my_inline 100 = if [ " :: (inline) PASS" puts cr ] [ " :: (inline) FAIL" puts # 12. Host Arguments & Host Exit "Testing host arguments and exit..." puts cr -hsargs isquot [ hsargs qlen 0 > ] and if [ +hsargs isq [ hsargs qlen 0 > ] and if [ " hsargs PASS" puts cr ] [ " hsargs FAIL" puts cr