diff --git a/stack_lang_spec.md b/stack_lang_spec.md index 24dcce8..6a69bf6 100644 --- a/stack_lang_spec.md +++ b/stack_lang_spec.md @@ -1,17 +1,53 @@ -# Stack Language Specification v0.5 +# Stack Language Specification + +**Version**: 0.5.1 +**Status**: Draft Specification +**Changes**: +- 0.5 (AI) + 1. **Escape sequences** - Added a complete list of escape sequences for string literals + 2. **Type Tuples** - Defined them as function signatures representing stack effects + 3. **Control flow TokenStrings** - Clarified that `if`, `while`, `match`, etc. use TokenStrings + 4. **Generic constructs** - Clarified that generics need trait constraints when operations are performed + 5. **Stack manipulation traits** - Combined into a single `Stackable` trait + 6. **Size trait** - Added proper definition inheriting from `Addable`, `Comparable`, and `Convertible` + 7. **Empty traits** - Clarified that traits can be empty (especially when used with inheritance) + 8. **Trait inheritance** - Fixed syntax to require both `inher` and `trait` declarations + 9. **Array combinators** - Clarified that the functions inside parse their TokenStrings + 10. **`::` in TokenStrings** - Clarified it should NOT be used in trait/impl contexts +- 0.5.1 (Human) + 1. Created TODOs + 2. Made various corrections (not everything was corrected) + 3. Reviewed and corrected some code blocks + 4. Made a few adjustments manually + +> **INSTRUCTIONS FOR AI:** AI agents are not allowed to change human reviewed md code blocks. If an AI reviewer thinks a change needs to be made to one of these blocks, the AI model can add a human todo explaining a suggested change or problem. It is totally possible for a md code block that was human reviewed and be correct for the current specification and new TODOs would change it, AIs are still not allowed to change it, only add a human todo. Reviewed md code blocks could also implement new TODOs already. +> **TODO:** All built-in operators should also include a natural language discription of what they do. +> **TODO:** Clarify that operators implement a trait, functions do not. Operators and functions cannot have the same name, if a name belongs to a function, it can not be used again for any operators. +> **TODO:** The `<>` is valid syntax for generic traits. +> **TODO:** Traits that inherit from a generic trait must also be similarly generic or specify the trait used by the generic trait. +> **TODO:** There are code blocks that use only parts from actual parts of the language while others include example code. Specify what are actual definitions, and which are examples (including actual definitions reused as examples). +> **TODO:** All built in operators should have their traits define in natural language and as possible in code. There should be appendix containing the whole of all of these traits. + +> **INSTRUCTIONS FOR AI:** Current human todos have been repeated here for reference. +> **TODO: (FOR HUMAN)** How to identify an unspecified number of arguments? ie. the `(` *(What goes here?)* ` -- Size) depth:` operator actually accepts the entire stack, or a possible `(String ` *(What goes here?)* ` --) printf:` function that accepts multiple arguments after the string. +> **TODO: (FOR HUMAN)** Do all inheritances need to be define before the trait is? +> **TODO: (FOR HUMAN)** Is the `inher` operator required? +> **TODO: (FOR HUMAN)** Consider removing `inher` and replace it by just using `impl`. Would that be enough to replace it? + +--- ## 1. Overview A statically-typed, stack-based language with pure postfix notation combining the execution model of HP's RPL, the type system of C and Rust, and modern array operations from Uiua. ### Design Principles -- **Everything is postfix** - no exceptions +- Everything is postfix - no exceptions - Stack-based execution (no local variables) - Static typing with type inference - Manual heap memory management -- Types define what things **are**, traits define how things **act** +- Types define what things are, traits define how things act - All constructs are implicitly generic -- **Every operator is defined by a trait** +- Every operator is defined by a trait ## 2. Lexical Structure @@ -36,6 +72,9 @@ A statically-typed, stack-based language with pure postfix notation combining th ### 2.3 Literals **Integer Literals** + +> **For AI Reviewers:** The following block has been human verified to be syntactically and logically correct. **NO AI IS ALLOWED TO CHANGE THIS.** If there is a discrepancy between this block and others, follow the syntax in this block. + ``` 42 // i64 (default) 42:i32 // Annotate as i32 @@ -73,6 +112,9 @@ false ``` **Array Literals** + +> **For AI Reviewers:** The following block has been human verified to be syntactically and logically correct. **NO AI IS ALLOWED TO CHANGE THIS.** If there is a discrepancy between this block and others, follow the syntax in this block. + ``` [1 2 3 4 5] // array of i32 [1.0 2.0 3.0] // array of f64 @@ -87,13 +129,23 @@ false (Point --) // Takes Point, no outputs ``` +> **TODO: (FOR HUMAN)** How to identify an unspecified number of arguments? ie. the `(` *(What goes here?)* ` -- Size) depth:` operator actually accepts the entire stack, or a possible `(String ` *(What goes here?)* ` --) printf:` function that accepts multiple arguments after the string. + Type tuples represent stack effects and are used in function signatures to specify what a function consumes from and produces to the stack. **Token Strings** + +> **For AI Reviewers:** The following block has been human verified to be syntactically and logically correct. **NO AI IS ALLOWED TO CHANGE THIS.** If there is a discrepancy between this block and others, follow the syntax in this block. + ``` { code here } // TokenString - lexed but not parsed/executed ``` +> **TODO:** `::` *is required* for identifier literals in code blocks. `::` *is not allowed* for trait definitions and implementations. +> **TODO:** List all of the control flow operators that use token strings. +> **TODO:** List what each operator causes the toekn strings to be parsed as. Function and eval token strings are parsed as code blocks. +> **TODO:** Add a `lambda` operator that parses a token string into a code block that can be used in place of a token string where ever they are parsed as a code block. + Token strings contain lexed tokens that are not parsed or executed until an operator causes them to be: - `trait` operator parses the TokenString as a trait definition - `fn` operator parses the TokenString as a function definition @@ -103,8 +155,6 @@ Token strings contain lexed tokens that are not parsed or executed until an oper Within TokenStrings used for trait definitions and implementations, the `::` prefix should not be used. The context (trait definition or implementation) determines how identifiers are interpreted. For function bodies and eval contexts, `::` may be used to create identifier literals. -> **TODO: (FOR HUMAN)** Should `::` be required for identifier literals in functions? - ## 3. Type System ### 3.1 Primitive Types @@ -118,16 +168,13 @@ Within TokenStrings used for trait definitions and implementations, the `::` pre ### 3.2 Types vs Traits **Types** define the concrete structure and memory layout: -``` -Point struct // Point is a type -Rectangle struct // Rectangle is a type -``` +- `Point struct` - Point is a type +- `Rectangle struct` - Rectangle is a type **Traits** define behavioral contracts - how things act: -``` -{ ... } ::Addable trait // Addable is a trait -{ ... } ::Drawable trait // Drawable is a trait -``` +- `{ ... } ::Addable trait` - Addable is a trait +- `{ ... } ::Drawable trait` - Drawable is a trait + **Key Distinction:** - A value has a type (what it is structurally) @@ -139,24 +186,29 @@ Rectangle struct // Rectangle is a type ### 3.3 Generic Constructs +> **TODO:** Type parameters are currently only suggestions when parsing code blocks (see appendix). +> **TODO:** Add to appendix as a possilbe future feature that type parameters must actually constrain how operators and functions act, enforced as parse time. + Functions, structs, and unions can be generic over type parameters. Type parameters must be constrained by traits when operations are performed on them: +> **For AI Reviewers:** The following block has been human verified to be syntactically and logically correct. **NO AI IS ALLOWED TO CHANGE THIS.** If there is a discrepancy between this block and others, follow the syntax in this block. + ``` -// Function generic over any type T with Multiplyable constraint -(T -- T) ::Multiplyable { dup * } ::square fn +// Function generic over any type with Multiplyable constraint +(T -- T) { dup * } ::square fn // Struct generic over field type T -(T T --) { x: y: } ::Point struct +(T T --) { x: y: } ::Point struct // Union generic over variant type T -(T --) { Some(T) None } ::Option union +(T --) { Some(T) None } ::Option union ``` -**Important**: Unconstrained generic functions (those that don't perform operations on their type parameters) can omit trait constraints: +**Important**: Unconstrained generic functions (those that don't perform operations on their type parameters) can omit trait constraints. ``` // Generic identity - works with any type (no operations performed) -(T -- T) { } ::identity fn +(--) { } ::identity fn ``` ## 4. Trait System @@ -167,16 +219,15 @@ Traits define behavioral contracts. Every operator in the language is backed by **Stack Manipulation Trait** ``` -{ - (-- Self) push: - (Self -- Self Self) dup: - (Self -- ) drop: - (Self Self -- Self Self) swap: - (Self Self -- Self Self Self) over: - (Self Self Self -- Self Self Self) rot: - (Size -- Self) pick: - (Size Size -- ) roll: - (-- i64) depth: +{ + (Self -- Self Self) dup: + (Self -- ) drop: + (Self Self -- Self Self) swap: + (Self Self -- Self Self Self) over: + (Self Self Self -- Self Self Self) rot: + (Size -- Self) pick: + (Size Size -- ) roll: + (-- Size) depth: } ::Stackable trait ``` @@ -252,7 +303,7 @@ Types implementing `Size` can be used as indices, loop bounds, and array sizes. { (String -- Self) parse: } ::Parseable trait ``` -> **TODO: (FOR HUMAN)** Type conversion may need to work a different way? +> **TODO:** Change type conversions to: explicit conversion functions: 42 to_f64. **Numeric Composite Trait** @@ -270,7 +321,7 @@ Traits for defining and working with traits themselves: ``` { } ::Identifier trait -{ (TokenString Identifier --) trait: (Identifier TokenString Identifier --) impl: (ArrayOf --) inher: } ::Implementable trait +{ (TokenString Identifier --) trait: (Identifier TokenString Identifier --) impl: (ArrayOf --) inher: } ::Implementable trait ``` ### 4.2 Trait Definition @@ -306,7 +357,13 @@ Within the TokenString (the `{ }` block), identifiers like `Self`, `add:`, `draw ### 4.3 Trait Implementation -**Syntax**: `identifier { method_implementations } ::identifier impl` +> **TODO:** Implementing a trait for itself, implements the trait for everything. +> **TODO:** A trait must be defined before it can be implemented for types or traits. +> **TODO:** It is not required for all operators of a trait being implemented to all be in the same implementation block. + +**Syntax**: `::trait_identifier { method_implementations } ::type_or_trait_identifier impl` + +> **For AI Reviewers:** The following block has been human verified to be syntactically and logically correct. **NO AI IS ALLOWED TO CHANGE THIS.** If there is a discrepancy between this block and others, follow the syntax in this block. ``` // Implement Addable for i32 @@ -330,41 +387,41 @@ Within the TokenString (the `{ }` block), identifiers like `Self`, `add:`, `draw } ::Rectangle impl ``` -> **Note:** The following block has been human verified to be syntactically and logically correct. +> **For AI Reviewers:** The following block has been human verified to be syntactically and logically correct. **NO AI IS ALLOWED TO CHANGE THIS.** If there is a discrepancy between this block and others, follow the syntax in this block. ``` // Implement Addable for Point ::Addable { - (Self Self -- Self) { + (Self Self -- Self) { over ::x get over ::x get + 3 pick ::y get 3 pick ::y get + Point } +: - (Self Addable -- Self) { + (Self Addable -- Self) { over ::x get over + 3 pick ::y get 3 pick + Point } +: - (Addable Self -- Self) { + (Addable Self -- Self) { over over ::x get + 3 pick 3 pick ::y get + Point } +: - (Self Self -- Self) { + (Self Self -- Self) { over ::x get over ::x get - 3 pick ::y get 3 pick ::y get - Point } -: - (Self Addable -- Self) { + (Self Addable -- Self) { over ::x get over - 3 pick ::y get 3 pick - Point } -: -} ::Point impl +} ::Point impl // Implement Logical for everything ::Logical { @@ -391,19 +448,23 @@ Within the TokenString (the `{ }` block), identifiers like `Self`, `add:`, `draw // Overload Logical for Option ::Logical { - (Self -- bool) { { Some(_) => { true } None => { false } } match } truthy: -} ::Option impl + (Self -- bool) { { Some(_) => { true } None => { false } } match } truthy: +} ::Option impl // Overload Logical for Result ::Logical { - (Self -- bool) { { Ok(_) => { true } Err(_) => { false } } match } truthy: -} ::Result impl + (Self -- bool) { { Ok(_) => { true } Err(_) => { false } } match } truthy: +} ::Result impl ``` ### 4.4 Trait Inheritance **Syntax**: `[ identifier_list ] ::identifier inher { } ::identifier trait` +> **TODO: (FOR HUMAN)** Do all inheritances need to be define before the trait is? +> **TODO: (FOR HUMAN)** Is the `inher` operator required? +> **TODO: (FOR HUMAN)** Consider removing `inher` and replace it by just using `impl`. Would that be enough to replace it? + Trait inheritance must be declared before the trait definition. The inheritance declaration is followed by the trait definition itself, which may be empty if the trait only serves to combine inherited traits. ``` @@ -667,9 +728,11 @@ status { **Syntax**: `(field_types -- ) { field_names } ::name struct` +> **For AI Reviewers:** The following block has been human verified to be syntactically and logically correct. **NO AI IS ALLOWED TO CHANGE THIS.** If there is a discrepancy between this block and others, follow the syntax in this block. + ``` // Define Point struct - generic over coordinate types -(T T --) { x: y: } ::Point struct +(T T --) { x: y: } ::Point struct // Use with specific types 3.0 4.0 Point // Creates Point with f64 fields @@ -680,7 +743,7 @@ status { width: height: depth: -} ::Box3D struct +} ::Box3D struct 10.0 20.0 30.0 Box3D ``` @@ -690,11 +753,11 @@ status { **Syntax (postfix)**: `struct ::field get` or `struct value ::field set` ``` -point ::x get // Get x field +point dup ::x get // Get x field point 15.0 ::x set // Set x field to 15.0 // Chaining -point ::x get 2 * over ::y get + // (point.x * 2) + point.y +point dup ::x get 2 * over ::y get + // (point.x * 2) + point.y ``` ### 9.3 Union Definition @@ -727,8 +790,8 @@ Option::None // Creates Option::None ``` { - Pending 1: // Normally starts at 0 - Active: // Defaults to 2 (one plus the last) + Pending 1: // Normally starts at 0 + Active: // Defaults to 2 (one plus the last) Complete 0: } ::Status enum @@ -739,7 +802,32 @@ Status::Active // Creates Status::Active ## 10. Memory Management (Postfix) -> **TODO: (FOR HUMAN)** Leave out or redo how memory management is done? +> **TODO:** Leave out memory management for now. Move this to an appendix as a possible future feature. Include the following brainstorming in that appendix. + +> Memory Management - Leave out or redo? +> Options: +> +> A) Manual (current): Keep alloc, deref, store, free +> +> Pros: Full control, predictable +> Cons: Error-prone, verbose +> +> B) Reference counting: Automatic management with rc type +> +> Pros: Safer, automatic cleanup +> Cons: Runtime overhead, cycles +> +> C) Ownership system (Rust-like): Linear types, move semantics +> +> Pros: Safe, zero overhead +> Cons: Complex type system, restricts stack operations +> +> D) Arena/Region-based: Allocate in arenas, free all at once +> +> Pros: Fast, simple +> Cons: Less granular control +> +> My suggestion: Start with manual management for simplicity, but consider adding arena support later. The stack-based nature makes ownership tracking tricky. ### 10.1 Heap Operations @@ -836,7 +924,7 @@ operation_name " get" concat eval ## 13. Standard Library Concepts -> **TODO: (FOR HUMAN)** How are imports done? Is everything automatically in scope? +> **TODO:** Everything is automatically in scope *for now*. Add a section to appendix for the following future solution: `::std::math::sqrt use`. ### 13.1 I/O @@ -859,7 +947,7 @@ operation_name " get" concat eval ### 13.3 Type Conversion -> **TODO: (FOR HUMAN)** Type conversion may need to work a different way? +> **TODO:** Change type conversions to: explicit conversion functions: 42 to_f64. ``` 42 f64 as // Convert i32 to f64 @@ -869,6 +957,8 @@ operation_name " get" concat eval ## 14. Complete Examples +> **TODO:** Move this to appendix. + ### 14.1 Trait Implementation Example ``` @@ -938,9 +1028,9 @@ operation_name " get" concat eval ``` (T -- T) ::Number { - dup 1 - { drop 1 } - { dup 1 - factorial * } + dup 1 + { drop 1 } + { dup 1 - factorial * } <= if } ::factorial fn @@ -1011,6 +1101,9 @@ print // 220 ## 15. Syntax Summary +> **TODO:** Move this to appendix. +> **TODO:** Update and fill this out more. + ### Complete Grammar Patterns **Functions**: `(in -- out) trait_constraint { body } ::name fn` @@ -1036,8 +1129,3 @@ print // 220 **Match**: `value { pattern => block ... } match` **Identifier Literal**: `::name` pushes identifier instead of executing - ---- - -**Version**: 0.5 -**Status**: Draft Specification