v0.3.6: map, filter, a lot of fixes

This commit is contained in:
Luxferre
2026-07-10 09:51:29 +03:00
parent 7b30ddacbd
commit 6c3a48d796
7 changed files with 54 additions and 7 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ Clyx aims for maximum portability and can be implemented in any modern runtime e
The core spec of Clyx is mostly stable but the standard library is still a work-in-progress. The core spec of Clyx is mostly stable but the standard library is still a work-in-progress.
The most recent Clyx version is **0.3.5**. The version is bumped every time the language core words (aka primitives), the standard library or the REPL get updated. The primitive set is expected to be indefinitely frozen once the version number reaches 1.0. The most recent Clyx version is **0.3.6**. The version is bumped every time the language core words (aka primitives), the standard library or the REPL get updated. The primitive set is expected to be indefinitely frozen once the version number reaches 1.0.
## Language features ## Language features
+18 -2
View File
@@ -261,7 +261,15 @@ func (c *Clyx) execQForm(q []any) {
func (c *Clyx) popCallerToken() any { func (c *Clyx) popCallerToken() any {
var stream *[]any var stream *[]any
if len(c.tokenStreams) > 0 { stream = &c.tokenStreams[len(c.tokenStreams)-1] } else { stream = &c.tokens } for i := len(c.tokenStreams) - 1; i >= 0; i-- {
if len(c.tokenStreams[i]) > 0 {
stream = &c.tokenStreams[i]
break
}
}
if stream == nil {
stream = &c.tokens
}
if len(*stream) == 0 { return nil } if len(*stream) == 0 { return nil }
t := (*stream)[0] t := (*stream)[0]
*stream = (*stream)[1:] *stream = (*stream)[1:]
@@ -278,7 +286,15 @@ func (c *Clyx) popCallerToken() any {
func (c *Clyx) cmdDefw() { func (c *Clyx) cmdDefw() {
var stream *[]any var stream *[]any
if len(c.tokenStreams) > 0 { stream = &c.tokenStreams[len(c.tokenStreams)-1] } else { stream = &c.tokens } for i := len(c.tokenStreams) - 1; i >= 0; i-- {
if len(c.tokenStreams[i]) > 0 {
stream = &c.tokenStreams[i]
break
}
}
if stream == nil {
stream = &c.tokens
}
var val any var val any
if len(*stream) > 0 { if len(*stream) > 0 {
t0 := (*stream)[0] t0 := (*stream)[0]
+14 -2
View File
@@ -102,14 +102,26 @@ class Clyx:
def _exec_loop(self, c, b): def _exec_loop(self, c, b):
while (self._exec_qform(c), self._stack.pop())[1]: self._exec_qform(b) while (self._exec_qform(c), self._stack.pop())[1]: self._exec_qform(b)
def _pop_caller_token(self): def _pop_caller_token(self):
t = self._token_streams[-1] if self._token_streams else self._tokens t = None
for stream in reversed(self._token_streams):
if stream:
t = stream
break
if t is None:
t = self._tokens
if not t: return None if not t: return None
val = t.pop(0) val = t.pop(0)
if val == '[': return self._parse_qform(t) if val == '[': return self._parse_qform(t)
if isinstance(val, str): return self._norm_token(val) if isinstance(val, str): return self._norm_token(val)
return val return val
def _cmd_defw(self): def _cmd_defw(self):
t = self._token_streams[-1] if self._token_streams else self._tokens t = None
for stream in reversed(self._token_streams):
if stream:
t = stream
break
if t is None:
t = self._tokens
val = self._pop_caller_token() if (t and (t[0] == '[' or isinstance(t[0], list))) else self._stack.pop() val = self._pop_caller_token() if (t and (t[0] == '[' or isinstance(t[0], list))) else self._stack.pop()
if isinstance(val, str): val = self._norm_token(val) if isinstance(val, str): val = self._norm_token(val)
name = self._stack.pop() name = self._stack.pop()
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "clyx" name = "clyx"
version = "0.3.5" version = "0.3.6"
description = "Clyx programming language interpreter" description = "Clyx programming language interpreter"
readme = "README.md" readme = "README.md"
requires-python = ">=3.7" requires-python = ">=3.7"
+9 -1
View File
@@ -2,7 +2,7 @@
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. 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 **52 standard library words** present in the reference implementation. This document describes the stack effects and behavior of the **50 primitive core words**, **3 bootstrapped core words**, and **56 standard library words** present in the reference implementation.
The Clyx runtime itself allows the programmer to redefine any already defined word, including the core ones and the ones from the standard library. Unlike e.g. Forth, where words are resolved at compile-time, Clyx words are resolved at runtime, so such redefinitions may affect all previously established behavior. That's why, as a rule, any **predefined** Clyx word, both in the language core and the standard library, must not exceed 6 characters. This is done to encourage programmers to define their own human-readable words (7+ characters long) without any risk of overwriting existing word definitions. The Clyx runtime itself allows the programmer to redefine any already defined word, including the core ones and the ones from the standard library. Unlike e.g. Forth, where words are resolved at compile-time, Clyx words are resolved at runtime, so such redefinitions may affect all previously established behavior. That's why, as a rule, any **predefined** Clyx word, both in the language core and the standard library, must not exceed 6 characters. This is done to encourage programmers to define their own human-readable words (7+ characters long) without any risk of overwriting existing word definitions.
@@ -546,6 +546,14 @@ These words are defined in the standard library file `lib.clx` and loaded dynami
- **Stack Effect**: `( length -- [q] )` - **Stack Effect**: `( length -- [q] )`
- **Description**: Pops the `length` integer from the stack, creates a contiguous Q-form of that length where every element is the number `0`, and pushes it back onto the stack. If `length` is less than or equal to `0`, returns an empty Q-form. - **Description**: Pops the `length` integer from the stack, creates a contiguous Q-form of that length where every element is the number `0`, and pushes it back onto the stack. If `length` is less than or equal to `0`, returns an empty Q-form.
#### `map`
- **Stack Effect**: `( [list] [op] -- [res] )` or `( [list] name -- [res] )`
- **Description**: Applies the operation `[op]` (either an executable Q-form or a defined word name) to each element of the Q-form `[list]`. Pushes the resulting Q-form `[res]` back onto the stack.
#### `filter`
- **Stack Effect**: `( [list] [op] -- [res] )` or `( [list] name -- [res] )`
- **Description**: Filters elements of the Q-form `[list]` by applying the predicate operation `[op]` (either an executable Q-form or a defined word name) to each element. Returns a new Q-form `[res]` containing only the elements for which the predicate returned a truthy value.
### 10.9 Bitwise Operations ### 10.9 Bitwise Operations
#### `bitnot` (Bitwise NOT) #### `bitnot` (Bitwise NOT)
+2
View File
@@ -57,3 +57,5 @@
:: qfor [ dup qlen while [ uncons drop [ [] cons cons ] dip swap dup 0 qget dip uncons drop swap 0 qget dup qlen ] drop drop ] :: qfor [ dup qlen while [ uncons drop [ [] cons cons ] dip swap dup 0 qget dip uncons drop swap 0 qget dup qlen ] drop drop ]
:: for [ next next [ i ] dip swap qfor ] :: for [ next next [ i ] dip swap qfor ]
:: allot [ [0] swap * ] :: allot [ [0] swap * ]
:: map [ swap [] rot isq not if [ [] cons ] [] [ swap cons ] lcat rot qfor rev ]
:: filter [ swap [] rot isq not if [ [] cons ] [] dup [ dup isq if [ qlen 1 - qget ] [] ] dip swap dup [ isnum ] 0 qget streq swap dup [ isstr ] 0 qget streq swap [ isq ] 0 qget streq or or if [ [ nip if [ swap cons ] [ drop ] ] ] [ [ if [ swap cons ] [ drop ] ] ] [ [ dup ] swap lcat ] dip lcat rot qfor rev ]
+9
View File
@@ -203,6 +203,12 @@ if [
] ]
2 5 range [ 2 3 4 ] streq 5 2 range [] streq and 5 5 range [] streq and if [ " range PASS" puts cr ] [ " range FAIL" puts cr ] # Test range 2 5 range [ 2 3 4 ] streq 5 2 range [] streq and 5 5 range [] streq and if [ " range PASS" puts cr ] [ " range FAIL" puts cr ] # Test range
3 allot [ 0 0 0 ] streq 0 allot [] streq and if [ " allot PASS" puts cr ] [ " allot FAIL" puts cr ] # Test allot 3 allot [ 0 0 0 ] streq 0 allot [] streq and if [ " allot PASS" puts cr ] [ " allot FAIL" puts cr ] # Test allot
[ 1 2 3 ] [ 2 * ] map [ 2 4 6 ] streq
[ 1 2 3 ] [ neg ] map [ -1 -2 -3 ] streq and
if [ " map PASS" puts cr ] [ " map FAIL" puts cr ] # Test map
[ 1 2 3 4 5 ] [ 3 > ] filter [ 4 5 ] streq
[ 1 2 3 4 5 ] [ isnum ] filter [ 1 2 3 4 5 ] streq and
if [ " filter PASS" puts cr ] [ " filter FAIL" puts cr ] # Test filter
# 9. Bitwise Operations # 9. Bitwise Operations
@@ -241,6 +247,9 @@ my_set_num 600 = and
set my_set_str "hello_set" set my_set_str "hello_set"
my_set_str "hello_set" streq and my_set_str "hello_set" streq and
if [ " set PASS" puts cr ] [ " set FAIL" puts cr ] # Test set if [ " set PASS" puts cr ] [ " set FAIL" puts cr ] # Test set
:: wrap_is [ is ]
wrap_is test_wrapped_const [ 888 ]
test_wrapped_const 888 = if [ " nested next PASS" puts cr ] [ " nested next FAIL" puts cr ]
" _start PASS" puts cr # Test _start (executed via entry point) " _start PASS" puts cr # Test _start (executed via entry point)
# 11. Host Language Evaluation: hseval # 11. Host Language Evaluation: hseval